home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / DCLAP 6d / dclap6d / network / nsclilib / ni_lib.c < prev    next >
Text File  |  1996-07-05  |  117KB  |  3,470 lines

  1. /*
  2. * ===========================================================================
  3. *
  4. *                            PUBLIC DOMAIN NOTICE
  5. *               National Center for Biotechnology Information
  6. *
  7. *  This software/database is a "United States Government Work" under the
  8. *  terms of the United States Copyright Act.  It was written as part of
  9. *  the author's official duties as a United States Government employee and
  10. *  thus cannot be copyrighted.  This software/database is freely available
  11. *  to the public for use. The National Library of Medicine and the U.S.
  12. *  Government have not placed any restriction on its use or reproduction.
  13. *
  14. *  Although all reasonable efforts have been taken to ensure the accuracy
  15. *  and reliability of the software and data, the NLM and the U.S.
  16. *  Government do not and cannot warrant the performance or results that
  17. *  may be obtained by using this software or data. The NLM and the U.S.
  18. *  Government disclaim all warranties, express or implied, including
  19. *  warranties of performance, merchantability or fitness for any particular
  20. *  purpose.
  21. *
  22. *  Please cite the author in any work or product based on this material.
  23. *
  24. * ===========================================================================
  25. *
  26. * File Name:    ni_lib.c
  27. *
  28. * Author:       Beatty, Gish, Epstein
  29. *
  30. * Version Creation Date:        1/1/92
  31. *
  32. * $Revision: 4.0 $
  33. *
  34. * File Description:
  35. *   This file is a library of functions to be used by server application
  36. *   and client software, using the NCBI "network services" paradigm.
  37. *
  38. *
  39. * Modifications:
  40. * --------------------------------------------------------------------------
  41. * Date     Name        Description of modification
  42. * -------  ----------  -----------------------------------------------------
  43. * 4/27/92  Epstein     Added extensive in-line commentary, and removed all tabs.
  44. * 5/11/92  Epstein     Removed unused function NI_SVCRequestGet(); added support
  45. *                      for the connection ID to be written to a CONID file each
  46. *                      time that the value of conid is updated; in practice,
  47. *                      only dispatcher will update a CONID file.
  48. * 6/22/92  Epstein     For UNIX signals, catch the SIGPIPE error which can
  49. *                      occur when writing to a socket which is no longer
  50. *                      connected.
  51. * 7/06/92  Epstein     Changed sokselectw() to examine the SO_ERROR socket option
  52. *                      after select()-ing a socket to which we were attempting a
  53. *                      connection. This eliminates "false connects", i.e.,
  54. *                      unsuccessful connection attempts which look successful
  55. *                      because the select() call returns 1.
  56. * 7/14/92  Epstein     Changed NI_SetDispatcher() and NI_InitServices() to use
  57. *                      a configurable timeout parameter, and in the process
  58. *                      also changed sokselectw() to have a timeout parameter,
  59. * 1/21/93  Epstein     Add dispatcher-list support, and add dispatcher-list
  60. *                      parameter to NI_InitServices().
  61. * 2/12/93  Epstein     Use new boolean parameter to MsgMakeHandle(), indicating
  62. *                      whether or not it should create a socket.  This was
  63. *                      an attempted fix for a Mac problem ... it later
  64. *                      turned out to be an incorrect problem-fix, but also
  65. *                      does no harm.
  66. * 2/24/93  Epstein     Fix long-standing Mac bug, by correctly destroying
  67. *                      services handle and hence closing an open socket.
  68. * 3/02/93  Epstein     Add functions to write dispatcher-configuration info
  69. *                      to a config file.  This provides a standardized
  70. *                      mechanisms which applications may use for net services
  71. *                      configuration.  Also added platform functions, so
  72. *                      that dispatcher/server complex can know what type
  73. *                      of platform a client is running on, assuming that the
  74. *                      client is telling the truth.
  75. * 3/03/93  Epstein     Cleanup variable initialization.
  76. * 3/08/93  Epstein     Improve error messages & cleanup to NI_InitServices,
  77. *                      include reason in login failure message, and add
  78. *                      client platform to service request.
  79. * 3/09/93  Epstein     Add HaltServices() function to simplify cleanup.
  80. * 3/22/93  Epstein     Fix typecast for getsockopt(), and, more importantly,
  81. *                      remember to return the computed value in NI_GetPlatform.
  82. * 3/23/93  Epstein     Support VMS/TGV, and add NETP_INET_ prefixes to 
  83. *                      conditional-compilation symbols.
  84. * 3/24/93  Epstein     Clear the caller's pointer in NI_SetDispConfig().
  85. * 3/31/93  Epstein     Add dispatcher pointer as context for all network
  86. *                      services operations; this allows an application
  87. *                      to use more than one dispatcher at a time, at the
  88. *                      expense of slightly greater complexity.  Also add
  89. *                      a "Generic Init" function, which can be used by
  90. *                      an application to obtain network-services in a
  91. *                      simplified, standardized manner.
  92. * 3/31/93  Epstein     Move debug and module variables to their correct home.
  93. * 4/02/93  Epstein     Add WinSock support.
  94. * 4/12/93  Schuler     Add MAKEWORD macro.
  95. * 4/21/93  Schuler     Removed function prototypes for NI_AsnRead, NI_AsnWrite
  96. * 5/07/93  Epstein     Move WSAStartup() code to a better place, add workaround
  97. *                      for connection attempt on a non-blocking socket in PC-NFS
  98. *                      4.0, add more platform definitions.
  99. * 5/24/93  Epstein     Add separate error codes for TCP/IP initialization
  100. *                      failure and inability to resolve local host name.
  101. * 5/25/93  Epstein     Add configuration-file workaround for PC-NFS 5.0 bug,
  102. *                      where NIS sometimes fails on the PC's own host name.
  103. * 5/27/93  Epstein     Incorporate pragmatic "Gestalt" code for Vibrant
  104. *                      scrolling workaround for WinSock under Windows 3.1,
  105. *                      add add SOCK_INDEX_ERRNO macro to workaround another
  106. *                      WinSock pecularity.
  107. * 6/02/93  Schuler     Change "Handle" to "MonitorPtr" for Monitors.
  108. * 6/07/93  Epstein     Added generic timer functions.
  109. *                      Also add missing revision history, derived from
  110. *                      RCS file.
  111. * 6/09/93  Epstein     Added activity hook to report network activity back
  112. *                      to an application.
  113. * 6/14/93  Epstein     Changed "Generic" logic to cause UNIX/VMS loginname
  114. *                      to override loginname, rather than vice versa.  Also
  115. *                      setup DispatchConnect() logic to set client's declared
  116. *                      IP address to 0.0.0.0, rather than causing an error,
  117. *                      in the case where the client cannot resolve its own
  118. *                      host name.  In this case, the dispatcher will set its
  119. *                      own opinion of the client address based upon
  120. *                      getpeername().
  121. * 6/15/93  Epstein     Eliminate "Gestalt" code for Vibrant scrolling
  122. *                      workaround for WinSock under Windows 3.1, since the
  123. *                      solution for this problem does not require its use.
  124. * 6/25/93  Epstein     Fix activity-hook action for service disconnection (had
  125. *                      erroneously announced dispatcher-disconnection), and
  126. *                      add logic to try to avoid getservbyname() by looking
  127. *                      up dispatcher port # and (loport,hiport) in NCBI
  128. *                      configuration file instead of in NIS.  As a last resort,
  129. *                      look up the name in NIS if the entry in the NCBI
  130. *                      config. file is non-numeric.  Also, change the client
  131. *                      port lookup mechanism for Macintoshes to add a configured
  132. *                      "delta" value to the low port number.  This results in
  133. *                      allowing several Network Services applications to run
  134. *                      concurrently on a Mac without port conflicts.
  135. * 7/08/93  Epstein     Fix list traversal error in NI_ProcessTimers()
  136. * 7/08/93  Epstein     Added a counter as a failsafe mechanism in
  137. *                      NI_ProcessTimers(), since previous fix attempt failed.
  138. * 7/09/93  Epstein     Changed a few #define names to avoid Alpha compilation
  139. *                      warnings, and added reference count to dispatcher data
  140. *                      structure.
  141. * 8/09/93  Epstein     Improve diagnostics when a listen() call fails
  142. * 8/23/93  Epstein     Add currentDisp variable so that the currently-attached
  143. *                      dispatcher is used when the parameter to NI_SetDispatcher
  144. *                      is NULL.
  145. * 8/31/93  Epstein     Fix host vs. network order when comparing port numbers.
  146. * 9/08/93  Epstein     Added new stackDescription variable, to be able to
  147. *                      report to the dispatcher the identity of the vendor
  148. *                      of the WinSock stack
  149. * 9/09/93  Epstein     Fix use of currentDisp variable to correctly compare
  150. *                      new dispatcher request again current dispatcher.
  151. *11/24/93  Epstein     Added code to support standalone servers and clients
  152. *                      which communicate with standalone servers or "service
  153. *                      brokers", which listen on a specific port (to be
  154. *                      augmented later).
  155. *11/30/93  Epstein     Made standalone server code UNIX-only, to avoid
  156. *                      possible compilation errors on other platforms.  However,
  157. *                      it should be possible in principle to run and test a
  158. *                      standalone server on a non-UNIX host.  Also, added
  159. *                      limited security to standalone servers.
  160. *12/08/93  Epstein     Fixed service connection activity hook, per discussion
  161. *                      with Kyle Hart.
  162. *01/19/94  Schuler     Post error (SEV_INFO) on WinSock initialization 
  163. *                      failure showing WinSock's error code.
  164. *01/28/94  Schuler     Replace "NETP_INET_MACTCP" with "NETP_INET_MACTCP"
  165. *01/28/94  Schuler     Defined THIS_MODULE and THIS_FILE
  166. *02/14/94  Epstein     Add preliminary RSAREF encryption support
  167. *02/22/94  Epstein     Add DISP_RECONN_ACTION logic to allow users to breakout
  168. *                      or quit if unable to contact primary dispatcher.
  169. *02/24/94  Epstein     Make use of new NI_DupPubKey function, and insert
  170. *                      newlines in macros to make editing easier.
  171. *03/03/94  Epstein     Reduce memory leaks, suppress non-printing characters
  172. *                      in winsock.dll.
  173. *04/22/94  Epstein     Change error handling to use SEV_ERROR and SEV_WARNING
  174. *                      (ErrPostEx).  Also do a better job of detecting
  175. *                      inability to connect to dispatcher in DispatchConnect(),
  176. *                      because under Solaris getsockopt() doesn't correctly
  177. *                      detect an error.
  178. *04/25/94  Epstein     Cosmetic change for error when NACK received from
  179. *                      Dispatcher.
  180. *05/04/94  Epstein     Add logic to allow a mixture of encrypted and
  181. *                      unencrypted services, determined by ENCRYPTION=FALSE
  182. *                      fields in the appropriate sections in the config.
  183. *                      files.
  184. *06/08/94  Epstein     Add SOCKS support (probably not correct yet) by
  185. *                      asking Dispatcher to provide a SVC_PRE_RESPONSE message
  186. *                      which contains the server's IP address.
  187. *06/10/94  Epstein     More SOCKS refinement, plus added tracing for SOCKS
  188. *06/15/94  Epstein     Produce working SOCKS version, by changing the protocol
  189. *                      so that a SOCKSified client uses two service request
  190. *                      messages; one to learn the IP address of the server
  191. *                      to which it will be assigned, and the second "real"
  192. *                      service request after it has bound the 'listen' port
  193. *                      on the SOCKS daemon.
  194. *07/01/94  Epstein     Determine at runtime whether or not to use SOCKS,
  195. *                      methodology, based upon presence or absence of
  196. *                      SOCKS_CONF file.
  197. *07/07/94  Epstein     Updated commentary.
  198. *09/22/94  Epstein     Improved standalone server code by using SO_REUSEADDR
  199. *                      option, making it possible for identical servers to
  200. *                      run consecutively without waiting for sockets to
  201. *                      shutdown.
  202. *12/02/94  Epstein     Changed NI_GenericGetService() to have environment
  203. *                      variables override configuration file for SERVICE_NAME
  204. *                      and RESOURCE_TYPE.  This is mostly just a convenience
  205. *                      for internal NCBI use, allowing the use of a single
  206. *                      config. file while using scripts to force the use
  207. *                      of a different service.
  208. *12/06/94  Epstein     Added connectDelay, adminInfo and motd fields.
  209. *                      The client reports the time which it took to
  210. *                      establish a connection in their connectDelay field.
  211. *                      The Dispatcher provides the name of the Network
  212. *                      administrator and a secondary message-of-the-day in
  213. *                      the other two fields.
  214. *12/21/94  Epstein     Added instrumentation for socket management
  215. *01/11/95  Epstein     Change socket instrumentation to suppress some
  216. *                      errors for WinSock 1.1.
  217. *01/13/95  Epstein     Add new GetLAPType() function to report Mac clients'
  218. *                      TCP/IP implementation ("Ethernet", "PPP", etc.) to
  219. *                      the Dispatcher.
  220. *03/20/95  Epstein     Fix InitServices/EndServices logic to correctly only
  221. *                      establish a single Dispatcher connection for multiple
  222. *                      services.
  223. *03/29/95  Epstein     Reduce calls to Message() in favor of ErrPostEx().
  224. *06/02/95  Epstein     Fix bindPort() to address host/network byte ordering;
  225. *                      this should correct a byte-ordering problem on little-
  226. *                      endian hosts which use SOCKS.
  227. *06/06/95  Epstein     For UNIX, try to get the user's name from the USER
  228. *                      environment variable before inquiring getlogin();
  229. *                      this can save CPU time and contention on some systems,
  230. *                      since access to utmp can be slow.
  231. *06/12/95  Epstein     Another byte-ordering fix for SOCKS for the SOCKS
  232. *                      proxy's port number.
  233. * ==========================================================================
  234. *
  235. *
  236. * RCS Modification History:
  237. * $Log: ni_lib.c,v $
  238.  * Revision 4.0  1995/07/26  13:56:32  ostell
  239.  * force revision to 4.0
  240.  *
  241.  * Revision 1.74  1995/07/12  14:53:28  epstein
  242.  * Another byte-ordering fix for SOCKS
  243.  *
  244.  * Revision 1.73  1995/06/06  10:04:52  epstein
  245.  * use USER environment variable in lieu of getlogin()
  246.  *
  247.  * Revision 1.72  95/06/02  16:58:43  epstein
  248.  * fix bindPort() using htonl() to correct byte-ordering problem on little-endian clients which use SOCKS
  249.  * 
  250.  * Revision 1.71  95/05/17  17:52:18  epstein
  251.  * add RCS log revision history
  252.  * 
  253. */
  254.  
  255. #define _NCBINET_LOCAL_VARS
  256. #define THIS_MODULE g_nsclient_module
  257. #define THIS_FILE _this_file
  258. #define __NI_LIB__
  259.  
  260. #include "ncbinet.h"
  261. #include "ni_lib.h"
  262. #include "ni_msg.h"
  263.  
  264. char * g_nsclient_module = "nsclient";
  265. static char *_this_file = __FILE__;
  266.  
  267.                                 
  268. #define ERR_KEY_MISMATCH "The public encryption key received from the dispatcher does\n\
  269. not match what is on file.  There is a slight security risk\n\
  270. that this key is being presented by a \"spoofer\" rather than\n\
  271. the real dispatcher.  You may wish to contact the NCBI by\n\
  272. other means to determine whether this new key is valid.  Do\n\
  273. you wish to accept this as the new key and continue?"
  274. #define ERR_KEY_NOPREVKEY "A public encryption key was just received from the dispatcher,\n\
  275. but no key is currently on file.  There is a slight security\n\
  276. risk that this key is being presented by a \"spoofer\" rather\n\
  277. than the real dispatcher.  You may wish to contact the NCBI by\n\
  278. other means to determine whether this new key is valid.  Do\n\
  279. you wish to accept this as the new key and continue?"
  280.  
  281. #ifdef NETP_INET_NEWT
  282.  
  283. #define SIN_ADDR        sin_addr.S_un.S_addr
  284. #define H_ADDR_TYPE     Uint4Ptr
  285. #else
  286. #define SIN_ADDR        sin_addr
  287. #define H_ADDR_TYPE     struct in_addr *
  288. #endif
  289.  
  290. #ifdef WIN16
  291. #ifndef MAKEWORD
  292. #define MAKEWORD(a,b)   ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) <<8)) 
  293. #endif 
  294. #endif
  295.  
  296.  
  297. typedef struct NI_Timer {
  298.     time_t timeout;
  299.     NI_TimeoutHook hook;
  300.     Pointer hookParam;
  301. } NI_Timer, PNTR NI_TimerPtr;
  302.  
  303.  
  304. /* GLOBALS */
  305. static FILE             *conid_fp = NULL;           /* File pointer for CONID */
  306. static NodePtr timerHead = NULL;                    /* list of timers */
  307. static NI_NetServHook activityHook = NULL;
  308. static NI_DispatcherPtr currentDisp = NULL;
  309. static CharPtr stackDescription = NULL;
  310. static fd_set openfds;
  311.  
  312.  
  313. #ifdef NETP_INET_WSOCK
  314. static Int4 wsaStartupCount = 0;
  315. #endif
  316.  
  317. NILoginPtr              NI_MakeMsgLogin PROTO((void));
  318. static Int2             SetIdentity PROTO((NI_DispatcherPtr disp, CharPtr user, CharPtr group, CharPtr domain));
  319. static void             HaltServices PROTO((NI_DispatcherPtr disp));
  320. static NI_HandPtr       DispatchConnect PROTO((NI_DispatcherPtr disp, CharPtr host, CharPtr name, int timeout));
  321. static Uint2            bindPort PROTO((int sok, struct sockaddr_in PNTR sokadr, Int2 loport, Int2 hiport, Uint4 remoteHost));
  322. static Int2             CopyIdentity PROTO((NI_DispatcherPtr disp, NI_UidPtr uid));
  323.  
  324. /** ( these are prototyped in ni_msg.h   [GDS] )
  325. Int2                    NI_AsnRead PROTO((Pointer fd, CharPtr buf, Uint2 len));
  326. Int2                    NI_AsnWrite PROTO((Pointer fd, CharPtr buf, Uint2 len));
  327. **/
  328. int                     sokselectr PROTO((int fd));
  329. int                     sokselectw PROTO((int fd, int timeout));
  330.  
  331. int                     getAsnError PROTO((char * str));
  332. void                    SetConFilePtr PROTO((FILE *fp));
  333. void                    CloseConFile PROTO((void));
  334.  
  335.  
  336.  
  337. /*
  338.  * Purpose:     Specify which dispatcher a client should try to connect to
  339.  *
  340.  * Parameters:
  341.  *   disp           Usually NULL, the pointer to a pre-existing Dispatcher
  342.  *                  structure
  343.  *   host           Name of the host (Fully Qualified Domain Name) to use
  344.  *   svc            Name of the "service" to try to use on that host
  345.  *   dispserialnum  Serial number of dispatcher-list. Use -1 if no response
  346.  *                  list is desired, or 0 if the serial number is not known.
  347.  *
  348.  *
  349.  * Description:
  350.  *              Set up the dispatcher name which should be used, and the
  351.  *              name of the service on that dispatcher. If other parameters
  352.  *              have been specified previously, free the memory associated
  353.  *              with those names.
  354.  *
  355.  * Note:
  356.  *              There are useful defaults for "svc". When in doubt, call
  357.  *              this function with a second arguement of NULL.
  358.  */
  359.  
  360. NI_DispatcherPtr
  361. NI_SetDispatcher(NI_DispatcherPtr disp, CharPtr host, CharPtr svc, int timeout,
  362.                  Int4 dispserialnum, ValNodePtr encryption)
  363. {
  364.     if (disp == NULL) {
  365.         if (currentDisp != NULL)
  366.         { /* use current dispatcher if it matches what the caller wants */
  367.             if (StringCmp(svc, currentDisp->dispServiceName) == 0 &&
  368.                 StringCmp(host, currentDisp->dispHostName) == 0) {
  369.                 return currentDisp;
  370.             }
  371.         }
  372.  
  373.         disp = (NI_DispatcherPtr) MemNew(sizeof(NI_Dispatcher));
  374.         if (disp == NULL)
  375.             return NULL;
  376.         disp->reqResponse = NULL;
  377.         disp->dispHostName = NULL;
  378.         disp->dispServiceName = NULL;
  379.         disp->dispSerialNo = 0;
  380.         disp->localHostAddr[0] = '\0';
  381.         disp->dispHP = NULL;
  382.         disp->svcsHP = NULL;
  383.         disp->clientPort = 0;
  384.         disp->identity = NULL;
  385.         disp->dispTimeout = 0;
  386.         disp->referenceCount = 0;
  387.         disp->brokeredDummy = FALSE;
  388.         disp->encryptInfo = encryption;
  389.         disp->useSocks = FALSE;
  390. #ifdef SOCKS_CONF
  391.         {
  392.             FILE *fp;
  393.  
  394.             if ((fp = FileOpen(SOCKS_CONF, "r")) != NULL)
  395.             {
  396.                 disp->useSocks = TRUE;
  397.                 FileClose(fp);
  398.             }
  399.         }
  400. #endif /* SOCKS_CONF */
  401.     }
  402.     if (disp->dispHostName != NULL) {
  403.         MemFree(disp->dispHostName);
  404.         disp->dispHostName = NULL;
  405.     }
  406.     if (disp->dispServiceName != NULL) {
  407.         MemFree(disp->dispServiceName);
  408.         disp->dispServiceName = NULL;
  409.     }
  410.     if (host != NULL)
  411.         disp->dispHostName = StringSave(host);
  412.     if (svc != NULL)
  413.         disp->dispServiceName = StringSave(svc);
  414.  
  415.     disp->dispSerialNo = dispserialnum;
  416.     disp->dispTimeout = timeout;
  417.  
  418.     return disp;
  419. } /* NI_SetDispatcher */
  420.  
  421.  
  422.  
  423. /*
  424.  * Purpose:     Try to establish a connection to the dispatcher
  425.  *
  426.  * Parameters:
  427.  *   disp           A pointer to the dispatcher structure
  428.  *   user           User name to try on the dispatcher
  429.  *   group          Group name to try on the dispatcher
  430.  *   password       Password for this user name
  431.  *   dip            A pointer to the caller's list of dispatchers; this should
  432.  *                     be used by the caller to update its information
  433.  *                     regarding which dispatchers to try in the future
  434.  *                     (if dip == NULL, then no retries will be made to get
  435.  *                      alternate dispatchers)
  436.  *
  437.  * Returns:
  438.  *                 -1, if something failed (ni_errno indicates the nature of
  439.  *                     the problem)
  440.  *                 0, if everything was successful
  441.  *                 1, if we are connected to the dispatcher which we requested,
  442.  *                     but the list of current dispatchers has changed
  443.  *                 2, if we are connected to a dispatcher, but not the one
  444.  *                     which we requested
  445.  *
  446.  *
  447.  * Description:
  448.  *              Perform any WinSock and/or SOCKS initialization as necessary
  449.  *              Connect to the dispatcher
  450.  *              Set-up a socket for an incoming connection from a server
  451.  *                  application process (non-SOCKS clients only)
  452.  *              Send a LOGIN message to the dispatcher
  453.  *              Wait for an ACK or NACK response from the dispatcher (or for
  454.  *                  a timeout to occur)
  455.  *              If the response was a NACK due to the dispatcher being a
  456.  *                  backup dispatcher, then try the dispatcher which it
  457.  *                  directs us to
  458.  */
  459.  
  460. Int2
  461. NI_InitServices(NI_DispatcherPtr disp, CharPtr user, CharPtr group, CharPtr password, NI_DispInfoPtr PNTR dip)
  462. {
  463.     NIMsgPtr            mp, imp;
  464.     NILoginPtr          loginp;
  465.     struct sockaddr_in  svcsAddr;
  466.     struct timeval      timeout;
  467.     int                 ready;
  468.     NIDispInfoPtr       dispinfo = NULL;
  469.     Boolean             newDispToTry;
  470.     Int2                altDispTries = 0;
  471.     Int2                retval = 0;
  472.     int                 status;
  473.     fd_set              readfds;
  474.     NI_PubKeyPtr        pubKey = NULL;
  475.     Boolean             failed;
  476. #ifdef NETP_INET_WSOCK
  477.     WSADATA wsaData;
  478. #endif /* NETP_INET_WSOCK */
  479. #if defined(OS_MAC) && defined(NETP_INET_MACTCP)
  480.     extern char * GetLAPType(void);
  481.     char *lapType = GetLAPType();
  482. #endif /* OS_MAC && NETP_INET_MACTCP */
  483.  
  484.     if (disp == NULL)
  485.     {
  486.         ni_errno = NIE_MISC;
  487.         return -1;
  488.     }
  489.  
  490.     if (disp->referenceCount > 0 && disp->dispHP != NULL)
  491.     { /* already connected */
  492.         disp->referenceCount++;
  493.         return 0;
  494.     }
  495.  
  496. #ifdef NETP_INET_WSOCK
  497.     status = WSAStartup(MAKEWORD(1,1),&wsaData);
  498.     if (status != 0)
  499.         ErrPostEx(SEV_ERROR,0,0,"WinSock 1.1 initialization failure, code %d", status-WSABASEERR);
  500.     /* Try WinSock 1.1 and 1.0 in that order of preference */
  501.     if (status != 0 && (status = WSAStartup(MAKEWORD(1,0),&wsaData)) != 0)
  502.     {
  503.         ErrPostEx(SEV_ERROR,0,0,"WinSock 1.0 initialization failure, code %d", status-WSABASEERR);
  504.         ni_errno = NIE_TCPINITFAIL;
  505.         return -1;
  506.     }
  507.     TRACE("%s\n", wsaData.szDescription);
  508.     if (stackDescription != NULL)
  509.     {
  510.         MemFree(stackDescription);
  511.     }
  512.     stackDescription = StringSave(wsaData.szDescription);
  513.     for (status = StrLen(stackDescription) - 1; status >= 0; status--)
  514.     {
  515.         /* convert characters which are incompatible with VisibleString */
  516.         if (stackDescription[status] < ' ' || stackDescription[status] > '~')
  517.             stackDescription[status] = '#';
  518.     }
  519.     wsaStartupCount++;
  520.  
  521. #endif
  522. #if defined(OS_MAC) && defined(NETP_INET_MACTCP)
  523.     if (lapType != NULL)
  524.     {
  525.         stackDescription = StringSave(lapType);
  526.     }
  527. #endif /* OS_MAC && NETP_INET_MACTCP */
  528. #ifdef NETP_SOCKS
  529.     if (disp->useSocks)
  530.     {
  531.         char path[128];
  532.  
  533.         Nlm_ProgramPath(path, sizeof path);
  534.  
  535.         SOCKSinit(path);
  536.         TRACE("Performed SOCKSinit(%s)\n", path);
  537.     }
  538. #endif /* NETP_SOCKS */
  539.  
  540.     if (disp->dispHostName == NULL)
  541.         disp->dispHostName = StringSave(NI_DEFAULT_HOST);
  542.     if (disp->dispServiceName == NULL)
  543.         disp->dispServiceName = StringSave(NI_DEFAULT_SERVICE);
  544.  
  545.     do {
  546.         newDispToTry = FALSE;
  547.         disp->svcsHP = NULL;
  548.         if ((disp->dispHP = DispatchConnect(disp, disp->dispHostName, disp->dispServiceName, disp->dispTimeout))
  549.             == NULL) {
  550.             NI_DestroyDispInfo(dispinfo);
  551.             HaltServices (disp);
  552.             ErrPostEx(SEV_WARNING,0,0, "NI_InitServices: Unable to connect to host <%s>, error <%s>", disp->dispHostName, ni_errlist[ni_errno]);
  553.             return -1;      /* ni_errno remains set */
  554.         }
  555.  
  556.         if ((disp->svcsHP = MsgMakeHandle(TRUE)) == NULL) {
  557.             NI_DestroyDispInfo(dispinfo);
  558.             HaltServices (disp);
  559.             ErrPostEx (SEV_ERROR, CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: Unable to allocate resources to communicate with %s", disp->dispHostName);
  560.             return -1;
  561.         }
  562.  
  563.         if (disp->dispTimeout > 0)
  564.         {
  565.             MsgSetReadTimeout(disp->svcsHP, disp->dispTimeout);
  566.         }
  567.  
  568.         if (! disp->useSocks)
  569.         { /* must defer binding and listening for SOCKS connection */
  570.     
  571.             if ((disp->clientPort = bindPort(disp->svcsHP->sok, &svcsAddr, disp->loport, disp->hiport, 0)) == 0) {
  572.                 MsgDestroyHandle(disp->svcsHP);
  573.                 disp->svcsHP = NULL;
  574.                 ni_errno = NIE_NOBIND;                  /* can't bind a free application socket */
  575.                 NI_DestroyDispInfo(dispinfo);
  576.                 HaltServices (disp);
  577.                 ErrPostEx (SEV_ERROR, CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  578.                 return -1;
  579.             }
  580.             if ((status = NI_LISTEN(disp->svcsHP->sok, 5)) < 0) {
  581. #ifdef NETP_INET_NEWT
  582.                 SOCK_ERRNO = ABS(status);
  583. #endif
  584.                 StringCpy(ni_errtext, sys_errlist[SOCK_INDEX_ERRNO]);
  585.                 ni_errno = NIE_NOLISTEN;
  586.                 NI_DestroyDispInfo(dispinfo);
  587.                 HaltServices (disp);
  588.                 ErrPostEx (SEV_ERROR, CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s> <port %d, errno %d>", ni_errlist[ni_errno], (int) disp->clientPort, (int) SOCK_ERRNO);
  589.                 return -1;
  590.             }
  591.         }
  592.  
  593.         SetIdentity(disp, user, group, NI_DEFAULT_DOMAIN);
  594.  
  595.         loginp = NI_MakeMsgLogin();
  596.         NI_DestroyUid(loginp->uid);
  597.         loginp->uid = NI_MakeUid();
  598.         loginp->seqno = disp->dispHP->seqno++;
  599.         loginp->dispserialno = disp->dispSerialNo;
  600.         loginp->connectDelay = disp->dispHP->connectDelay;
  601.         if (disp->encryptInfo != NULL && NI_EncrAvailable())
  602.         {
  603.             loginp->encryptionDesired = TRUE;
  604.             if (disp->encryptInfo->data.ptrvalue != NULL)
  605.             {
  606.                 pubKey = (NI_PubKeyPtr) disp->encryptInfo->data.ptrvalue;
  607.                 loginp->pubKey = (NIPubKeyPtr) NI_PubKeyDup(pubKey);
  608.             }
  609.         }
  610.         CopyIdentity(disp, loginp->uid);
  611.         if (password != NULL)
  612.             loginp->password = StringSave(password);
  613.         mp = MsgBuild(NI_LOGIN, disp->dispHP->conid, (VoidPtr) loginp);
  614.  
  615.         if (MsgWrite(disp->dispHP, mp, FALSE) < 0) {
  616.             if (getAsnError(ni_errtext) == ECONNRESET)
  617.                 ni_errno = NIE_MAXCONNS;
  618.             else
  619.                 ni_errno = NIE_MSGWRITE;
  620.             MsgDestroyHandle(disp->svcsHP);
  621.             disp->svcsHP = NULL;
  622.             NI_DestroyDispInfo(dispinfo);
  623.             HaltServices (disp);
  624.             ErrPostEx (SEV_ERROR, CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  625.             return -1;
  626.         }
  627.  
  628.         /* blocks until ACK or ERROR from dispatcher or TIMEOUT */
  629.  
  630.         timeout.tv_sec = (Uint4) NI_TIMEOUT_SECS;
  631.         timeout.tv_usec = 0;
  632.         FD_ZERO(&readfds);
  633.         FD_SET(disp->dispHP->sok, &readfds);
  634.         while ((ready = NI_select(FD_SETSIZE, &readfds, NULL, NULL, &timeout)) < 0) {
  635.             if (SOCK_ERRNO == EINTR)
  636.                 ;                   /* repeat while interrupted */
  637.             else {
  638.                 MsgDestroyHandle(disp->svcsHP);
  639.                 disp->svcsHP = NULL;
  640.                 ni_errno = NIE_SELECT;              /* select error */
  641.                 NI_DestroyDispInfo(dispinfo);
  642.                 ErrPostEx (SEV_ERROR, CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  643.                 return -1;
  644.             }
  645.         }
  646.  
  647.         if (FD_ISSET(disp->dispHP->sok, &readfds) != 0) {
  648.             if ((imp = MsgRead(disp->dispHP, FALSE)) == NULL) {
  649.                 if (getAsnError(ni_errtext) == ECONNRESET)
  650.                     ni_errno = NIE_MAXCONNS;
  651.                 else
  652.                     ni_errno = NIE_MSGREAD;
  653.  
  654.                 MsgDestroyHandle(disp->svcsHP);
  655.                 disp->svcsHP = NULL;
  656.                 NI_DestroyDispInfo(dispinfo);
  657.                 HaltServices (disp);
  658.                 ErrPostEx (SEV_ERROR, CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  659.                 return -1;
  660.             }
  661.             switch (imp->type) {
  662.               case NI_ACK:
  663.                 /************************************************************/
  664.                 /* even though we connected successfully to the dispatcher, */
  665.                 /* it may have given us more up-to-date information on the  */
  666.                 /* latest list of dispatchers which should be tried; if so, */
  667.                 /* pass the updated list back to the caller                 */
  668.                 /************************************************************/
  669.                 if (imp->msun.ack->dispinfo != NULL) {
  670.                     if (dispinfo != NULL)
  671.                     {
  672.                         NI_DestroyDispInfo(dispinfo);
  673.                         dispinfo = NULL;
  674.                     }
  675.                     dispinfo = imp->msun.ack->dispinfo;
  676.                     imp->msun.ack->dispinfo = NULL; /* for clean free */
  677.                 }
  678.                 if (disp->encryptInfo != NULL && NI_EncrAvailable())
  679.                 {
  680.                     if (dispinfo != NULL && dispinfo->pubKey != NULL)
  681.                     {
  682.                         failed = FALSE;
  683.                         if (pubKey == NULL)
  684.                         {
  685.                             failed = Message(MSG_YN, ERR_KEY_NOPREVKEY) ==
  686.                                      ANS_NO;
  687.                             ni_errno = NIE_NEWKEYNOTACCPT;
  688.                         } else {
  689.                             if (! NI_PubKeysEqual(pubKey, (NI_PubKeyPtr) dispinfo->pubKey))
  690.                             {
  691.                                 failed = Message(MSG_YN, ERR_KEY_MISMATCH) ==
  692.                                          ANS_NO;
  693.                                 ni_errno = NIE_NEWKEYMISMATCH;
  694.                             }
  695.                         }
  696.                         if (failed)
  697.                         {
  698.                             HaltServices(disp);
  699.                             MsgDestroy(imp);
  700.                             return -1;
  701.                         } else {
  702.                             /* replace the key */
  703.                             NI_DestroyPubKey((NIPubKeyPtr) pubKey);
  704.                             pubKey = (NI_PubKeyPtr) dispinfo->pubKey;
  705.                             dispinfo->pubKey = NULL;
  706.                             disp->encryptInfo->data.ptrvalue = (Pointer) pubKey;
  707.                         }
  708.                     }
  709.                 }
  710.                 if (dispinfo != NULL && dip != NULL) {
  711.                     if (*dip != NULL)
  712.                         NI_DestroyDispInfo((NIDispInfoPtr) *dip);
  713.                     *dip = (NI_DispInfoPtr) dispinfo;
  714.                     dispinfo = NULL;
  715.                     retval = 1;
  716.                 }
  717.                 else {
  718.                     NI_DestroyDispInfo(dispinfo);
  719.                 }
  720.                 if (imp->msun.ack->motd != NULL &&
  721.                     imp->msun.ack->motd[0] != NULLB)
  722.                 {
  723.                     disp->motd = imp->msun.ack->motd;
  724.                     imp->msun.ack->motd = NULL; /* for clean free */
  725.                 }
  726.                 if (imp->msun.ack->adminInfo != NULL &&
  727.                     imp->msun.ack->adminInfo[0] != NULLB)
  728.                 {
  729.                     disp->adminInfo = imp->msun.ack->adminInfo;
  730.                     imp->msun.ack->adminInfo = NULL; /* for clean free */
  731.                 }
  732. #ifdef OS_UNIX
  733.                 signal(SIGPIPE, SIG_IGN); /* catch socket errors */
  734. #endif /* OS_UNIX */
  735.                 MsgDestroy(imp);
  736.                 disp->referenceCount++;
  737.                 if (currentDisp == NULL)
  738.                 {
  739.                     currentDisp = disp;
  740.                 }
  741.                 return retval;   /* only good return */
  742.  
  743.               case NI_NACK:
  744.                 ni_errno = (enum ni_error) imp->msun.nack->code;
  745.                 if (imp->msun.nack->reason != NULL)
  746.                 {
  747.                     StringCpy(ni_errtext, imp->msun.nack->reason);
  748.                 } else {
  749.                     ni_errtext[0] = '\0';
  750.                 }
  751.                 if (dispinfo != NULL)
  752.                 {
  753.                     NI_DestroyDispInfo(dispinfo);
  754.                     dispinfo = NULL;
  755.                 }
  756.                 dispinfo = imp->msun.nack->dispinfo;
  757.                 imp->msun.nack->dispinfo = NULL; /* for clean free */
  758.                 if (ni_errno == NIE_BACKUPDISP && dispinfo != NULL &&
  759.                     dispinfo->numdispatchers > 0 && dip != NULL &&
  760.                     ++altDispTries < MAX_ALT_DISP_TRIES)
  761.                 {
  762.                     MsgDestroy(imp);
  763.                     HaltServices (disp);
  764.                     NI_SetDispatcher(disp, dispinfo->displist[0], disp->dispServiceName,
  765.                                      disp->dispTimeout, dispinfo->serialno, disp->encryptInfo);
  766.                     newDispToTry = TRUE;
  767.                     retval = 2;
  768.                     break;
  769.                 }
  770.                 MsgDestroy(imp);
  771.                 MsgDestroyHandle(disp->svcsHP);
  772.                 disp->svcsHP = NULL;
  773.                 NI_DestroyDispInfo(dispinfo);
  774.                 HaltServices (disp);
  775.                 ErrPostEx (SEV_ERROR, CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>\n%s", ni_errlist[ni_errno], ni_errtext);
  776.                 return -1;
  777.  
  778.               default:
  779.                 MsgDestroy(imp);
  780.                 ni_errno = NIE_MSGUNK;
  781.                 MsgDestroyHandle(disp->svcsHP);
  782.                 disp->svcsHP = NULL;
  783.                 NI_DestroyDispInfo(dispinfo);
  784.                 HaltServices (disp);
  785.                 ErrPostEx (SEV_ERROR, CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  786.                 return -1;
  787.             }
  788.         }
  789.     } while (newDispToTry);
  790.  
  791.     MsgDestroyHandle(disp->svcsHP);
  792.     disp->svcsHP = NULL;
  793.     ni_errno = NIE_LOGTIMEOUT;          /* TIMEOUT */
  794.     NI_DestroyDispInfo(dispinfo);
  795.     HaltServices (disp);
  796.     ErrPostEx (SEV_ERROR, CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  797.     return -1;
  798. } /* NI_InitServices */
  799.  
  800.  
  801. /*
  802.  * Purpose:     Init network services based on information in config file
  803.  *
  804.  * Parameters:
  805.  *   configFile     Name of NCBI-style configuration file.  If NULL, defaults
  806.  *                     to "NCBI"
  807.  *   configSection  Section with NCBI-style configuration file.  If NULL,
  808.  *                     defaults to "NET_SERV"
  809.  *   showMonitor    Boolean; if TRUE, display a monitor while re-trying
  810.  *                     for an alternate dispatcher
  811.  *   lastDispatcher Pointer to where this function should store the name
  812.  *                  of the dispatcher which was actually used (may be NULL
  813.  *                  if the caller does not care about this value)
  814.  *   lastDispLen    Maximum length of lastDispatcher
  815.  *
  816.  * Returns:
  817.  *                 NULL, if unable to contact dispatcher
  818.  *                 a pointer to the Dispatcher structure, otherwise
  819.  *
  820.  *
  821.  * Description:
  822.  *              Extracts a dispatcher name and a user name from a configuration
  823.  *              file.  If necessary, tries other dispatchers, in order, as
  824.  *              listed in configuration file.  Also sets up encryption, if
  825.  *              the client is encryption-capable and encryption is requested
  826.  *              in the configuration file.
  827.  *
  828.  *
  829.  * Note:
  830.  *              This function is provided as a convenience to developers who
  831.  *              wish to use Network Services.  Use of this function is not
  832.  *              integral to the use of Network Services ... it is merely a
  833.  *              convenience.
  834.  */
  835.  
  836. NI_DispatcherPtr
  837. NI_GenericInit (CharPtr configFile, CharPtr configSection, Boolean showMonitor, CharPtr lastDispatcher, Int2 lastDispLen)
  838. {
  839.     char *def_user;
  840.     char username[64];
  841.     char groupname[20];
  842.     char password[20];
  843.     char dispname[60];
  844.     char disp_config[10];
  845.     char disp_msg[110];
  846.     char buf[60];
  847.     Boolean more_disps;
  848.     int alternate = 1;
  849.     int disp_timeout;
  850.     Int4 disp_serialno;
  851.     Nlm_Monitor *mon = NULL;
  852.     NI_DispInfoPtr dip = NULL;
  853.     NI_DispatcherPtr disp = NULL;
  854.     Boolean someBrokered;
  855.     ValNodePtr encryptInfo = NULL;
  856.     NI_PubKeyPtr keyCopy = NULL;
  857.     Boolean doEncr = FALSE;
  858.     Boolean quitOnDispConnFailure = FALSE;
  859.     Boolean showMessage = FALSE;
  860. #ifdef OS_UNIX
  861.     char *getlogin PROTO((void));
  862. #endif
  863.  
  864.     /******************* open the network connnection *********/
  865. #define NI_DISP_NAME "dispatch1.nlm.nih.gov"
  866. #define NI_USER_NAME "anonymous"
  867. #define NI_GROUP_NAME "GUEST"
  868.  
  869.     def_user = NI_USER_NAME;
  870.  
  871.     if (configFile == NULL)
  872.         configFile = "NCBI";
  873.     if (configSection == NULL)
  874.         configSection = "NET_SERV";
  875.  
  876.     Nlm_GetAppParam(configFile, configSection, "DISP_USERNAME", NI_USER_NAME, username,
  877.                 sizeof username);
  878.     /* the user's login name overrides the config file */
  879.     /* for UNIX or VMS systems (or, for the future, any system where the      */
  880.     /* user name can be determined), use the user's login name as the default */
  881.     def_user = NULL;
  882. #ifdef OS_UNIX
  883.     if ((def_user = getenv("USER")) == NULL)
  884.     {
  885.         def_user = getlogin();
  886.     }
  887. #endif
  888. #ifdef OS_VMS
  889.     def_user = getenv("USER");
  890. #endif
  891.     if (def_user != NULL)
  892.     {
  893.         StrNCpy(username, def_user, sizeof username);
  894.     }
  895.  
  896.     Nlm_GetAppParam(configFile, configSection, "DISP_GROUPNAME", NI_GROUP_NAME, groupname,
  897.                 sizeof groupname);
  898.     Nlm_GetAppParam(configFile, configSection, "DISP_PASSWORD", "", password,
  899.                 sizeof password); /* default = NONE */
  900.  
  901.     Nlm_GetAppParam(configFile, configSection, "DISP_TIMEOUT", "0", buf, sizeof buf);
  902.     disp_timeout = atoi(buf);
  903.  
  904.     Nlm_GetAppParam(configFile, configSection, "DISPSERIALNO", "0", buf, sizeof buf);
  905.     disp_serialno = atoi(buf);
  906.  
  907.     Nlm_GetAppParam(configFile, configSection, "DISPATCHER", NI_DISP_NAME, dispname,
  908.                 sizeof dispname);
  909.  
  910.     Nlm_GetAppParam(configFile, configSection, "SOME_BROKERED", "FALSE", buf, sizeof buf);
  911.     someBrokered = StrICmp(buf, "TRUE") == 0;
  912.  
  913.     Nlm_GetAppParam(configFile, configSection, "DISP_RECONN_ACTION", "CONT", buf, sizeof buf);
  914.     showMessage = StrICmp(buf, "ASK") == 0;
  915.     quitOnDispConnFailure = StrICmp(buf, "QUIT") == 0;
  916.  
  917.     Nlm_GetAppParam(configFile, configSection, "ENCRYPTION_DESIRED", "FALSE", buf, sizeof buf);
  918.     if (StrICmp(buf, "TRUE") == 0 && NI_EncrAvailable())
  919.     {
  920.         doEncr = TRUE;
  921.         encryptInfo = ValNodeNew(NULL);
  922.         encryptInfo->data.ptrvalue = (Pointer) NI_ReadPubKeyFromConfig();
  923.         keyCopy = NI_PubKeyDup((NI_PubKeyPtr) encryptInfo->data.ptrvalue);
  924.     }
  925.  
  926.     do {
  927.         if (alternate == 2 && showMonitor)
  928.         {
  929.             mon = MonitorStrNew("Unable to contact primary dispatcher", 35);
  930.         }
  931.         if (alternate >= 2)
  932.         {
  933.             if (showMessage)
  934.             {
  935.                 sprintf (disp_msg, "Unable to contact primary dispatcher.  Ready to try\ndispatcher #%d <", alternate);
  936.                 StrCat(disp_msg, dispname);
  937.                 StrCat(disp_msg, ">.  Continue?");
  938.                 if (Message(MSG_YN, disp_msg) == ANS_NO)
  939.                     break;
  940.             } else {
  941.                 sprintf(disp_msg, "Trying dispatcher #%d <", alternate);
  942.                 StrCat(disp_msg, dispname);
  943.                 StrCat(disp_msg, ">");
  944.                 if (showMonitor) {
  945.                     MonitorStrValue(mon, disp_msg);
  946.                 }
  947.             }
  948.         }
  949.  
  950.         if (lastDispatcher != NULL) {
  951.                 StrNCpy(lastDispatcher, dispname, lastDispLen);
  952.         }
  953.  
  954.         disp = NI_SetDispatcher (NULL, dispname, NULL, disp_timeout, disp_serialno, encryptInfo);
  955.         if (someBrokered)
  956.         {
  957.             disp->brokeredDummy = TRUE;
  958.             disp->someBrokered = TRUE;
  959.             disp->referenceCount++;
  960.             ValNodeFree (encryptInfo);
  961.             return disp;
  962.         }
  963.  
  964.         if (NI_InitServices(disp, username, groupname[0] == '\0' ? NULL : groupname,
  965.                             password[0] == '\0' ? NULL : password, &dip) >= 0)
  966.         {
  967.             if (dip != NULL && dip->serialno != disp_serialno) {
  968.                 NI_SetDispConfig (&dip, dispname, sizeof dispname);
  969.             }
  970.             if (mon != NULL)
  971.                 MonitorFree(mon);
  972.             if (disp->encryptInfo != NULL &&
  973.                 disp->encryptInfo->data.ptrvalue != NULL &&
  974.                 ! NI_PubKeysEqual(keyCopy,
  975.                  (NI_PubKeyPtr) disp->encryptInfo->data.ptrvalue))
  976.             {
  977.                 NI_WritePubKeyToConfig ((NI_PubKeyPtr) disp->encryptInfo->data.ptrvalue);
  978.             }
  979.             NI_DestroyPubKey ((NIPubKeyPtr) keyCopy);
  980.             return disp;
  981.         }
  982.         ErrShow ();
  983.         NI_EndServices (disp);
  984.         sprintf(disp_config, "DISP_ALT_%d", alternate++);
  985.         more_disps = Nlm_GetAppParam(configFile, configSection, disp_config, "", dispname,
  986.                                  sizeof dispname);
  987.         if (doEncr)
  988.         {
  989.             encryptInfo = ValNodeNew(NULL);
  990.             encryptInfo->data.ptrvalue = (Pointer)
  991.                                           NI_PubKeyDup((NI_PubKeyPtr) keyCopy);
  992.         }
  993.     } while (more_disps && ! quitOnDispConnFailure);
  994.  
  995.     if (mon != NULL)
  996.         MonitorFree(mon);
  997.  
  998.     ValNodeFree (encryptInfo);
  999.     NI_DestroyPubKey ((NIPubKeyPtr) keyCopy);
  1000.     ErrPostEx(SEV_ERROR,0,0, "NI_InitServices: Unable to connect to any dispatcher");
  1001.     return NULL;
  1002. }
  1003.  
  1004.  
  1005. /*
  1006.  * Purpose:     Get a network service based on information in config file
  1007.  *
  1008.  * Parameters:
  1009.  *   disp           Pointer to the dispatcher structure obtained from a
  1010.  *                  previous call to NI_SetDispatcher or NI_GenericInit
  1011.  *   configFile     Name of NCBI-style configuration file.  If NULL, defaults
  1012.  *                     to "NCBI"
  1013.  *   defService     The default service/resource/resource-type name, if
  1014.  *                  not specified otherwise in configuration file.
  1015.  *   hasResource    Boolean; if TRUE, ask for a resource when requesting
  1016.  *                     service
  1017.  *
  1018.  * Returns:
  1019.  *                 NULL, if unable to obtain service
  1020.  *                 a pointer to the service-structure, otherwise
  1021.  *
  1022.  *
  1023.  * Description:
  1024.  *              Extracts a service name and other service data from a
  1025.  *              configuration file, and attempts to obtain that service.
  1026.  *              As a special case, handle communication with a "brokered
  1027.  *              server" (a server which is already listening on a port, where
  1028.  *              no communication needs to be performed with the dispatcher).
  1029.  *              Also disable data encryption for this service request, if
  1030.  *              explicitly specified in the configuration file.
  1031.  *
  1032.  *
  1033.  * Note:
  1034.  *              This function is provided as a convenience to developers who
  1035.  *              wish to use Network Services.  Use of this function is not
  1036.  *              integral to the use of Network Services ... it is merely a
  1037.  *              convenience.
  1038.  *
  1039.  *              For UNIX systems, environment variables can be used to
  1040.  *              override the config. file's values for SERVICE_NAME and
  1041.  *              RESOURCE_TYPE.
  1042.  */
  1043.  
  1044. NI_HandPtr
  1045. NI_GenericGetService (NI_DispatcherPtr disp, CharPtr configFile, CharPtr configSection, CharPtr defService, Boolean hasResource)
  1046. {
  1047.     char buf[40];
  1048.     char service[40];
  1049.     char resource[40];
  1050.     char res_type[40];
  1051.     int serv_min;
  1052.     int serv_max;
  1053.     int res_min;
  1054.     int res_max;
  1055.     char brokeredIpaddr[40];
  1056.     Uint2 port;
  1057.     NI_HandPtr result;
  1058.     ValNodePtr savEncrypt;
  1059. #ifdef OS_UNIX
  1060.     CharPtr envName = MemNew(StrLen(configSection) + 20);
  1061.     CharPtr envValue;
  1062. #endif
  1063.  
  1064.     if (configFile == NULL)
  1065.         configFile = "NCBI";
  1066.  
  1067.     Nlm_GetAppParam(configFile, configSection, "SERVICE_NAME", defService,
  1068.             service, sizeof service);
  1069. #ifdef OS_UNIX
  1070.     /* environment variable overrides config. file */
  1071.     sprintf (envName, "NI_SERVICE_NAME_%s", configSection);
  1072.     if ((envValue = getenv(envName)) != NULL)
  1073.     {
  1074.         StrCpy (service, envValue);
  1075.     }
  1076. #endif /* OS_UNIX */
  1077.     Nlm_GetAppParam(configFile, configSection, "SERV_VERS_MIN", "1",
  1078.             buf, sizeof buf);
  1079.     serv_min = atoi(buf);
  1080.     Nlm_GetAppParam(configFile, configSection, "SERV_VERS_MAX", "0",
  1081.             buf, sizeof buf);
  1082.     serv_max = atoi(buf);
  1083.  
  1084.     res_min = 1;
  1085.     res_max = 0;
  1086.  
  1087.     if (hasResource) {
  1088.         Nlm_GetAppParam(configFile, configSection, "RESOURCE_NAME", defService,
  1089.                 resource, sizeof resource);
  1090.         Nlm_GetAppParam(configFile, configSection, "RESOURCE_TYPE", defService,
  1091.                 res_type, sizeof res_type);
  1092. #ifdef OS_UNIX
  1093.         /* environment variable overrides config. file */
  1094.         sprintf (envName, "NI_RESOURCE_TYPE_%s", configSection);
  1095.         if ((envValue = getenv(envName)) != NULL)
  1096.         {
  1097.             StrCpy (res_type, envValue);
  1098.         }
  1099. #endif /* OS_UNIX */
  1100.         Nlm_GetAppParam(configFile, configSection, "RES_VERS_MIN", "1",
  1101.                 buf, sizeof buf);
  1102.         res_min = atoi(buf);
  1103.         Nlm_GetAppParam(configFile, configSection, "RES_VERS_MAX", "0",
  1104.                 buf, sizeof buf);
  1105.         res_max = atoi(buf);
  1106.     }
  1107.  
  1108. #ifdef OS_UNIX
  1109.     MemFree (envName);
  1110. #endif /* OS_UNIX */
  1111.  
  1112.     if (disp->someBrokered)
  1113.     {
  1114.         Nlm_GetAppParam(configFile, configSection, "BROKERED_PORT", "0",
  1115.                     buf, sizeof buf);
  1116.         port = atoi(buf);
  1117.         Nlm_GetAppParam(configFile, configSection, "BROKERED_IPADDR", "",
  1118.                     brokeredIpaddr, sizeof brokeredIpaddr);
  1119.         if (port != 0 && brokeredIpaddr[0] != '\0')
  1120.         { /* simulate service request by connecting to that port */
  1121.             struct  sockaddr_in serv_addr;
  1122.             NI_HandPtr sHP;
  1123.             int timeout = 30;
  1124.             int status;
  1125.  
  1126.             MemFill((VoidPtr) &serv_addr, '\0', sizeof(serv_addr));
  1127.             serv_addr.sin_family = AF_INET;
  1128.             serv_addr.sin_addr.s_addr = inet_addr(brokeredIpaddr);
  1129.             serv_addr.sin_port = port;
  1130.  
  1131.             if ((sHP = MsgMakeHandle(TRUE)) == NULL)
  1132.                 return NULL;
  1133.             MsgSetLJError(sHP);
  1134.             sHP->hostname = StringSave(brokeredIpaddr);
  1135.  
  1136.             if (activityHook != NULL)
  1137.             {
  1138.                 activityHook(sHP, NetServHook_svcreq, 0);
  1139.             }
  1140.  
  1141.           RETRY:
  1142. #ifndef NETP_INET_NEWT
  1143.             if ((status = NI_CONNECT(sHP->sok, (struct sockaddr PNTR) &serv_addr, sizeof(serv_addr))) < 0) { /* } */
  1144. #else
  1145.             if ((status = NI_CONNECT(sHP->sok, &serv_addr, sizeof(serv_addr))) < 0) {
  1146.                 SOCK_ERRNO = ABS(status);
  1147. #endif
  1148.                 switch (SOCK_ERRNO) {
  1149.                   case EINTR:
  1150.                     goto RETRY;
  1151.  
  1152. #ifdef NETP_INET_PCNFS
  1153.                   /* This is apparently a bug in PC-NFS 4.0 ... a connection attempt */
  1154.                   /* on a non-blocking socket yields errno == 0                      */
  1155.                   case 0:
  1156. #endif /* NETP_INET_PCNFS */
  1157.                   case EWOULDBLOCK:
  1158.                   case EINPROGRESS:
  1159.                     /* if the connect()ion is not established immediately, a         */
  1160.                     /* select() can be performed where the corresponding "write"     */
  1161.                     /* file descriptor will be enabled once the connect()ion has been*/
  1162.                     /* established                                                   */
  1163.                     if (sokselectw(sHP->sok, timeout) == 0) {
  1164.                         return sHP;
  1165.                     }
  1166.                     break;
  1167.  
  1168.                   default:
  1169.                     break;
  1170.                 }
  1171.                 MsgDestroyHandle(sHP);
  1172.                 ni_errno = NIE_BROKSVCCONN;        /* can't connect to brokered service */
  1173.                 return NULL;
  1174.             }
  1175.             {
  1176.                 Char buf[16];
  1177.                 Char key[8];
  1178.                 Int2 len;
  1179.         
  1180.                 if ((len = Nlm_GetAppParam("NCBI", "NET_SERV", "DESKEY", "", buf, sizeof buf)) > 0 &&
  1181.                     AsnTypeStringToHex(buf, len, key))
  1182.                     
  1183.                 {
  1184.                     NI_SetupDESEncryption(sHP, (UcharPtr) key);
  1185.                 }
  1186.             }
  1187.             return sHP;
  1188.         } else {
  1189.             if (disp->brokeredDummy)
  1190.             { /* JAE ... establish a true dispatcher connection here */
  1191.             }
  1192.         }
  1193.     }
  1194.  
  1195.     savEncrypt = disp->encryptInfo;
  1196.     if (Nlm_GetAppParam(configFile, configSection, "ENCRYPTION", "TRUE",
  1197.                     buf, sizeof buf) > 0 && StrICmp(buf, "FALSE") == 0)
  1198.     {
  1199.         /* temporarily disable encryption */
  1200.         disp->encryptInfo = NULL;
  1201.     }
  1202.     result = NI_ServiceGet(disp, service, serv_min, serv_max,
  1203.                            hasResource ? resource : NULL, res_type, res_min,
  1204.                            res_max);
  1205.     disp->encryptInfo = savEncrypt;
  1206.  
  1207.     return result;
  1208. }
  1209.  
  1210.  
  1211. /*
  1212.  * Purpose:     Write dispatcher-configuration information to a config file
  1213.  *
  1214.  * Parameters:
  1215.  *   dipp           A pointer to the caller's list of dispatchers, obtained
  1216.  *                     from NI_InitServices()
  1217.  *   dispatcher     The caller's dispatcher string
  1218.  *   dispLen        Length of the caller's dispatcher string
  1219.  *
  1220.  * Returns:
  1221.  *                 0, if bad parameters were provided
  1222.  *                 the dispatcher-list serial number, otherwise
  1223.  *
  1224.  *
  1225.  * Description:
  1226.  *              Sets up the "NCBI" configuration file with the following
  1227.  *              entries in the "NET_SERV" section:
  1228.  *              * DISPATCHER is the primary dispatcher name
  1229.  *              * DISP_ALT_n for every alternate dispatcher, 1 <= n, a smaller
  1230.  *                n indicates a higher priority alternate dispatcher
  1231.  *              * DISPSERIALNO is the serial number of the dispatcher list
  1232.  *                obtained from a remote dispatcher. This serial number should
  1233.  *                be unique for all time ... the dispatcher's serial number
  1234.  *                must be changed whenever the master list is modified.
  1235.  *
  1236.  * Note:
  1237.  *              This configuration mechanism is only _one_ recommended
  1238.  *              mechanism for network services dispatcher configuration. The
  1239.  *              application may perform this configuration in any manner
  1240.  *              deemed appropriate by the application programmer.
  1241.  *
  1242.  *              The value returned by this function is the recommended value
  1243.  *              for the dispserialno parameter in a subsequent call to
  1244.  *              NI_InitDispatcher().
  1245.  */
  1246.  
  1247. Int4
  1248. NI_SetDispConfig(NI_DispInfoPtr PNTR dipp, CharPtr dispatcher, Int2 dispLen)
  1249. {
  1250.   Int2 num;
  1251.   Char dispConfig[20];
  1252.   char buf[10];
  1253.   Int4 retval;
  1254.   NI_DispInfoPtr dip;
  1255.  
  1256.   if (dipp == NULL || (dip = *dipp) == NULL)
  1257.   {
  1258.     if (dispatcher != NULL)
  1259.     {
  1260.       dispatcher[0] = '\0';
  1261.     }
  1262.     return 0;
  1263.   }
  1264.  
  1265.   if (dip->numdispatchers > 0 && dip->displist != NULL)
  1266.   {
  1267.     StringNCpy (dispatcher, dip->displist[0], dispLen);
  1268.     SetAppParam ("NCBI", "NET_SERV", "DISPATCHER", dip->displist[0]);
  1269.   }
  1270.  
  1271.   for (num = 1; num < dip->numdispatchers; num++)
  1272.   {
  1273.     sprintf (dispConfig, "DISP_ALT_%d", num);
  1274.     SetAppParam ("NCBI", "NET_SERV", dispConfig, dip->displist[num]);
  1275.   }
  1276.  
  1277.   /* wipe out any extraneous old configuration */
  1278.   for (num = dip->numdispatchers; num < 100; num++)
  1279.   {
  1280.     sprintf (dispConfig, "DISP_ALT_%d", num);
  1281.     if (Nlm_GetAppParam("NCBI", "NET_SERV", dispConfig, "", buf, sizeof buf) <= 0)
  1282.     {
  1283.       break;
  1284.     }
  1285.     SetAppParam ("NCBI", "NET_SERV", dispConfig, NULL);
  1286.   }
  1287.  
  1288.   retval = dip->serialno;
  1289.   sprintf (buf, "%ld", (long) dip->serialno);
  1290.   SetAppParam ("NCBI", "NET_SERV", "DISPSERIALNO", buf);
  1291.  
  1292.   NI_DestroyDispInfo ((NIDispInfoPtr) dip);
  1293.   *dipp = NULL;
  1294.  
  1295.   return retval;
  1296. }
  1297.  
  1298.  
  1299. /*
  1300.  * Purpose:     End use of network services
  1301.  *
  1302.  * Parameters:
  1303.  *   disp           A pointer to the dispatcher structure
  1304.  *
  1305.  * Returns:
  1306.  *                 0 (always)
  1307.  *
  1308.  *
  1309.  * Description:
  1310.  *              Tear down the sockets and data structures associated with
  1311.  *              the dispatcher and a server, and free all memory associated
  1312.  *              with data structures.
  1313.  */
  1314.  
  1315. Int2
  1316. NI_EndServices(NI_DispatcherPtr disp)
  1317. {
  1318.     Int2 openSockets;
  1319.  
  1320.     if (disp == NULL)
  1321.         return 0;
  1322.  
  1323.     if (disp->referenceCount > 0)
  1324.         disp->referenceCount--;
  1325.  
  1326.     if (disp->referenceCount <= 0)
  1327.     {
  1328.         if (disp == currentDisp)
  1329.         {
  1330.             currentDisp = NULL;
  1331.         }
  1332.     
  1333.         HaltServices (disp);
  1334.         NI_SetDispatcher(disp, NULL, NULL, 0, 0, NULL);               /* free mem */
  1335.     
  1336.         if (stackDescription != NULL)
  1337.         {
  1338.             MemFree(stackDescription);
  1339.             stackDescription = NULL;
  1340.         }
  1341.     
  1342.         MemFree(disp);
  1343.  
  1344.         /* For historical reasons pertaining to Network Entrez, a single open
  1345.            socket does not constitute an error in this context.  However, at
  1346.            program exit time, a single open socket does constitute a serious
  1347.            problem. */
  1348.         if ((openSockets = NI_SocketsOpen()) > 1)
  1349.         {
  1350.             ErrPostEx(SEV_WARNING,0,0, "At end-services time, %d sockets are still open", openSockets);
  1351.         }
  1352.     }
  1353.  
  1354.     return 0;
  1355. } /* NI_EndServices */
  1356.  
  1357.  
  1358.  
  1359. /*
  1360.  * Purpose:     Request a catalog from the dispatcher
  1361.  *
  1362.  * Parameters:
  1363.  *   disp           A pointer to the dispatcher structure
  1364.  *
  1365.  * Returns:
  1366.  *                 NULL, if unable to obtain the catalog
  1367.  *                 a pointer to the received catalog data structure, otherwise
  1368.  *
  1369.  *
  1370.  * Description:
  1371.  *              Send a request to the dispatcher, requesting a catalog, and
  1372.  *              wait (up to some timeout) for a response. The dispatcher's
  1373.  *              response should either be that catalog, or a NACK.
  1374.  */
  1375.  
  1376. NICatalogPtr
  1377. NI_GetCatalog(NI_DispatcherPtr disp)
  1378. {
  1379.     NICatalogPtr        catp;
  1380.     NIMsgPtr            mp, imp;
  1381.     NICmdPtr            cmdp;
  1382.     struct timeval      timeout;
  1383.     int                 ready;
  1384.     fd_set              readfds;
  1385.  
  1386.     if (disp == NULL)
  1387.         return NULL;
  1388.  
  1389.     cmdp = (NICmdPtr) NI_MakeMsgCmd();
  1390.     cmdp->seqno = disp->dispHP->seqno++;
  1391.     cmdp->code = NI_SEND_CATALOG;
  1392.     if ((mp = MsgBuild(NI_COMMAND, disp->dispHP->conid, (VoidPtr) cmdp)) == NULL) {
  1393.         ni_errno = NIE_MISC;    /* unable to alloc mem for Msg */
  1394.         return NULL;
  1395.     }
  1396.     if (MsgWrite(disp->dispHP, mp, FALSE) < 0) {
  1397.         ni_errno = NIE_MSGWRITE;
  1398.         return NULL;
  1399.     }
  1400.  
  1401.     /* blocks until response from dispatcher or TIMEOUT */
  1402.  
  1403.     timeout.tv_sec = (Uint4) NI_TIMEOUT_SECS;
  1404.     timeout.tv_usec = 0;
  1405.     FD_ZERO(&readfds);
  1406.     FD_SET(disp->dispHP->sok, &readfds);
  1407.     while ((ready = NI_select(FD_SETSIZE, &readfds, NULL, NULL, &timeout)) < 0) {
  1408.         if (SOCK_ERRNO == EINTR)
  1409.             ;                   /* repeat while interrupted */
  1410.         else {
  1411.             ni_errno = NIE_SELECT;              /* select error */
  1412.             return NULL;
  1413.         }
  1414.     }
  1415.  
  1416.     if (FD_ISSET(disp->dispHP->sok, &readfds) != 0) {
  1417.         if ((imp = MsgRead(disp->dispHP, FALSE)) == NULL) {
  1418.             LOG_SOCKET(disp->dispHP->sok, FALSE);
  1419.             NI_CLOSESOCKET(disp->dispHP->sok);
  1420.             ni_errno = NIE_MSGREAD;
  1421.             return NULL;
  1422.         }
  1423.         switch (imp->type) {
  1424.           case NI_CATALOG:
  1425.             catp = imp->msun.catalog;
  1426.             imp->msun.catalog = NULL;
  1427.             ni_errno = NIE_NO_ERROR;
  1428.             MsgDestroy(imp);
  1429.             return catp;
  1430.             break;
  1431.  
  1432.           case NI_NACK:
  1433.             ni_errno = (enum ni_error) imp->msun.nack->code;
  1434.             if (imp->msun.nack->reason != NULL)
  1435.                 StringCpy(ni_errtext, imp->msun.nack->reason);
  1436.             else
  1437.                 ni_errtext[0] = '\0';
  1438.             MsgDestroy(imp);
  1439.             return NULL;
  1440.  
  1441.           default:
  1442.             MsgDestroy(imp);
  1443.             ni_errno = NIE_MSGUNK;      /* Unknown MSG type */
  1444.             return NULL;
  1445.         }
  1446.     }
  1447.     ni_errno = NIE_CMDTIMEOUT;          /* TIMEOUT */
  1448.     return NULL;
  1449. } /* NI_GetCatalog */
  1450.  
  1451.  
  1452.  
  1453. /*
  1454.  * Purpose:     Create the data structure for a service request
  1455.  *
  1456.  * Parameters:
  1457.  *   disp           A pointer to the dispatcher structure
  1458.  *
  1459.  * Returns:
  1460.  *                 a pointer to the newly created data structure
  1461.  *
  1462.  *
  1463.  * Description:
  1464.  *              Allocate the memory for a service request data structure,
  1465.  *              and fill in some of the fields.
  1466.  * Note:
  1467.  *              There are two ways for a program to issue a service request:
  1468.  *              (1) Multi-step, general method (like IRS form 1040)
  1469.  *                * Build a request with NI_SVCRequestBuild()
  1470.  *                * Populate the request with a specific service request using
  1471.  *                  NI_RequestSetService()
  1472.  *                * Populate the request with zero or more resource requests
  1473.  *                  calling NI_RequestAddResource() once for every resource
  1474.  *                * Send the request with NI_ServiceRequest(), and (hopefully)
  1475.  *                  obtain a connection to a service provider
  1476.  *                * At some later time, delete the request (to save memory)
  1477.  *              (2) One-stop shopping, for simple requirement (like form 1040EZ)
  1478.  *                * Do everything for a service and up to one resource using
  1479.  *                  NI_ServiceGet()
  1480.  */
  1481.  
  1482. NI_ReqPtr
  1483. NI_SVCRequestBuild(NI_DispatcherPtr disp)
  1484. {
  1485.     NI_ReqPtr   reqp;
  1486.  
  1487.     if (disp == NULL)
  1488.         return NULL;
  1489.  
  1490.     reqp = (NI_ReqPtr) NI_MakeRequest();
  1491.     reqp->clientPort = (Uint2) disp->clientPort;
  1492.     if (disp->useSocks)
  1493.     {
  1494.         /* tell the Dispatcher that it should use getpeername() to determine */
  1495.         /* the IP address of the SOCKS daemon                                */
  1496.         reqp->clientAddr = StringSave("0.0.0.0");
  1497.     } else {
  1498.         reqp->clientAddr = StringSave(disp->localHostAddr);
  1499.     }
  1500.     reqp->dispatcher = disp; /* should not be freed when destroying Req */
  1501.  
  1502.     return  reqp;
  1503. } /* NI_SVCRequestBuild */
  1504.  
  1505.  
  1506.  
  1507. /*
  1508.  * Purpose:     Destroy a service request data structure
  1509.  *
  1510.  * Parameters:
  1511.  *   reqp         A pointer to the data structure to be destroyed
  1512.  *
  1513.  *
  1514.  * Description:
  1515.  *              Free all the resources associated with a service request
  1516.  */
  1517.  
  1518. void
  1519. NI_SVCRequestDestroy(NI_ReqPtr reqp)
  1520. {
  1521.     NI_DestroyRequest(reqp);
  1522. } /* NI_SVCRequestDestroy */
  1523.  
  1524.  
  1525.  
  1526. /*
  1527.  * Purpose:     Make a service request for a service and up to one resource
  1528.  *
  1529.  * Parameters:
  1530.  *   disp         A pointer to the dispatcher structure
  1531.  *   svc          Name of requested service
  1532.  *   svcvermin    Minimum version number requested for this service
  1533.  *   svcvermax    Maximum version number requested for this service
  1534.  *   res          Name of requested resource (possibly NULL)
  1535.  *   resvermin    Minimum version number requested for this resource
  1536.  *   resvermax    Maximum version number requested for this resource
  1537.  *
  1538.  * Returns:
  1539.  *                The result of the service request
  1540.  *
  1541.  *
  1542.  * Description:
  1543.  *              Create and issue a service request for the specified
  1544.  *              parameters.
  1545.  */
  1546.  
  1547. NI_HandPtr
  1548. NI_ServiceGet(NI_DispatcherPtr disp, CharPtr svc, Uint2 svcvermin, Uint2 svcvermax, CharPtr res, CharPtr restype, Uint2 resvermin, Uint2 resvermax)
  1549. {
  1550.     NI_ReqPtr   reqp;
  1551.  
  1552.     if (disp == NULL)
  1553.         return NULL;
  1554.  
  1555.     reqp = NI_SVCRequestBuild(disp);
  1556.     NI_RequestSetService(reqp, svc, svcvermin, svcvermax);
  1557.     if (res != NULL)
  1558.         NI_RequestAddResource(reqp, res, restype, resvermin, resvermax);
  1559.  
  1560.     return NI_ServiceRequest(reqp);
  1561. } /* NI_ServiceGet */
  1562.  
  1563.  
  1564.  
  1565. /*
  1566.  * Purpose:     Issue the specified service request
  1567.  *
  1568.  * Parameters:
  1569.  *   req          The pre-formatted service request
  1570.  *
  1571.  * Returns:
  1572.  *               A message handle to the server which is servicing our request,
  1573.  *                   if successful
  1574.  *               NULL, otherwise (ni_errno will indicate a more precise cause)
  1575.  *
  1576.  *
  1577.  * Description:
  1578.  *              Create and issue a service request for the specified
  1579.  *              service request, as follows:
  1580.  *              * Create a data structure to which the resulting service
  1581.  *                connection can be attached
  1582.  *              * Send the service request to the dispatcher
  1583.  *              * Wait for the following two events, in either order:
  1584.  *                (1) A response from the dispatcher, which is either a
  1585.  *                    SVC_RESPONSE (good), or a NACK (bad) {or a timeout}
  1586.  *                (2) A connection request from the server, which we then
  1587.  *                    accept()
  1588.  *              * If both of the two events occur successfully, return with
  1589.  *                success, else, return with failure.
  1590.  *
  1591.  * Note:
  1592.  *              If the caller's Dispatcher data structure indicates that
  1593.  *              encryption should be performed, then a DES key is
  1594.  *              pseudorandomly generated prior to issuing the service request,
  1595.  *              and is encrypted using public-key encryption.  Following
  1596.  *              successful establishment of the client<->server session, the
  1597.  *              DES key is used to encrypt the ensuing session using
  1598.  *              cypher-block-chaining.
  1599.  *
  1600.  *              For SOCKSified clients, a different protocol is used where
  1601.  *              the client first sends a "pre-service-request", asking the
  1602.  *              IP address of the computer to which the Dispatcher will assign
  1603.  *              the request.  Upon receipt of a NI_SVC_PRE_RESPONSE message
  1604.  *              containing that IP address, the client performs a SOCKSified
  1605.  *              bind()ing indicating that the specified IP address will
  1606.  *              "call back".  Having determine the port number which has
  1607.  *              been bound on the SOCKS proxy, the client sends the "real"
  1608.  *              service request containing the SOCKS proxy's port number
  1609.  *              and a reminder as to which IP address the Dispatcher
  1610.  *              "promised" would be assigned.  After that, processing
  1611.  *              proceeds normally, with the server connecting-back (via
  1612.  *              the SOCKS proxy) and the Dispatcher sending a SVC_RESPONSE
  1613.  *              acknowledgement.
  1614.  *
  1615.  *              Note that SOCKS and encryption are completely orthogonal
  1616.  *              with respect to each other, and a client may use either, both,
  1617.  *              or neither.
  1618.  */
  1619.  
  1620. NI_HandPtr
  1621. NI_ServiceRequest(NI_ReqPtr req)
  1622. {
  1623.     NI_HandPtr          sconnhp;
  1624. #ifdef NETP_INET_MACTCP
  1625.     Int4                sconnlen;
  1626. #else
  1627.     int                 sconnlen;
  1628. #endif
  1629.     struct sockaddr_in  sconnaddr;
  1630.     NIMsgPtr            mp, imp;
  1631.     NISvcReqPtr         svcreqp;
  1632.     struct timeval      timeout;
  1633.     int                 ready;
  1634.     Boolean             disp_contact = FALSE, serv_contact = FALSE;
  1635.     Uint4               this_req;
  1636.     fd_set              readfds;
  1637.     NI_DispatcherPtr    disp = req->dispatcher;
  1638.     Uchar               desKey[8];
  1639. #ifdef NETP_SOCKS
  1640.     struct sockaddr_in  svcsAddr;
  1641.     Int2                status;
  1642. #endif
  1643.  
  1644.     ni_errtext[0] = '\0';
  1645.     if ((sconnhp = MsgMakeHandle(FALSE)) == NULL) {
  1646.         ni_errno = NIE_MAKEHAND;
  1647.         return NULL;
  1648.     }
  1649.  
  1650.     svcreqp = NI_MakeMsgSvcreq();
  1651.     svcreqp->seqno = disp->dispHP->seqno++;
  1652.     svcreqp->platform = (Uint4) NI_GetPlatform();
  1653.     if (stackDescription != NULL)
  1654.     {
  1655.         svcreqp->applId = StringSave(stackDescription);
  1656.     }
  1657.     if (disp->encryptInfo != NULL && NI_EncrAvailable())
  1658.     {
  1659.         UcharPtr            encryptedDesKey;
  1660.         Int2                encryptedLen;
  1661.  
  1662.         NI_GenerateDESKey (desKey);
  1663.         encryptedLen = NI_PubKeyEncrypt((NI_PubKeyPtr) disp->encryptInfo->data.ptrvalue,
  1664.                          desKey, sizeof desKey, &encryptedDesKey);
  1665.         if (encryptedLen <= 0)
  1666.         {
  1667.             NI_DestroyRequest(req);
  1668.             MsgDestroyHandle(sconnhp);
  1669.             ni_errno = NIE_PUBKEYENCRFAIL;
  1670.             return NULL;
  1671.         }
  1672.         /* convert the DES key into a ByteStore */
  1673.         svcreqp->desKey = BSNew(encryptedLen);
  1674.         BSWrite (svcreqp->desKey, (VoidPtr) encryptedDesKey, encryptedLen);
  1675.         MemFree (encryptedDesKey);
  1676.     }
  1677.     this_req = svcreqp->seqno;
  1678.     CopyIdentity(disp, svcreqp->uid);
  1679.     NI_DestroyRequest(svcreqp->request);
  1680.     svcreqp->request = req;
  1681.  
  1682.     if (disp->useSocks)
  1683.     {
  1684.         svcreqp->wantPreResponse = TRUE;
  1685.     }
  1686.  
  1687.     if ((mp = MsgBuild(NI_SVC_REQUEST, disp->dispHP->conid, (VoidPtr) svcreqp)) == NULL) {
  1688.         NI_DestroyRequest(req);
  1689.         MsgDestroyHandle(sconnhp);
  1690.         ni_errno = NIE_MISC;            /* unable to alloc mem for Msg */
  1691.         return NULL;
  1692.     }
  1693.  
  1694.     if (MsgWrite(disp->dispHP, mp, svcreqp->wantPreResponse) < 0) {
  1695.         MsgDestroyHandle(sconnhp);
  1696.         ni_errno = NIE_MSGWRITE;
  1697.         return NULL;
  1698.     }
  1699.  
  1700.     /* blocks until SVC_RESPONSE from dispatcher and service or NACK or TIMEOUT */
  1701.  
  1702.     while (!disp_contact || !serv_contact) {
  1703.         timeout.tv_sec = (Uint4) NI_TIMEOUT_SECS;
  1704.         timeout.tv_usec = 0;
  1705.         FD_ZERO(&readfds);
  1706.         FD_SET(disp->dispHP->sok, &readfds);
  1707.         FD_SET(disp->svcsHP->sok, &readfds);
  1708.         while ((ready = NI_select(FD_SETSIZE, &readfds, NULL, NULL, &timeout)) < 0) {
  1709.             if (SOCK_ERRNO == EINTR)
  1710.                 ;                                       /* repeat while interrupted */
  1711.             else {
  1712.                 MsgDestroyHandle(sconnhp);
  1713.                 ni_errno = NIE_SELECT;          /* select error */
  1714.                 return NULL;
  1715.             }
  1716.         }
  1717.         if (ready == 0) {
  1718.             MsgDestroyHandle(sconnhp);
  1719.             ni_errno = NIE_DSPTIMEOUT;          /* TIMEOUT */
  1720.             return NULL;
  1721.         }
  1722.         if (FD_ISSET(disp->dispHP->sok, &readfds) != 0) {
  1723.             if ((imp = MsgRead(disp->dispHP, FALSE)) == NULL) {
  1724.                 LOG_SOCKET(disp->dispHP->sok, FALSE);
  1725.                 NI_CLOSESOCKET(disp->dispHP->sok);
  1726.                 MsgDestroyHandle(sconnhp);
  1727.                 ni_errno = NIE_MSGREAD;
  1728.                 return NULL;
  1729.             }
  1730.             disp_contact = TRUE;
  1731.             switch (imp->type) {
  1732.               case NI_SVC_PRE_RESPONSE:
  1733. #ifdef NETP_SOCKS
  1734.                 if (disp->useSocks)
  1735.                 {
  1736.                     /* must defer binding and listening for SOCKS connection */
  1737.                     /* until server's IP address is known                    */
  1738.             
  1739.                     TRACE("Processing SOCKS SVC_PRE_RESPONSE\n");
  1740.                     /* SOCKS can't deal well with non-blocking connections */
  1741.                     NI_SETBLOCKING(disp->svcsHP->sok);
  1742.     
  1743.                     if ((disp->clientPort = bindPort(disp->svcsHP->sok, &svcsAddr, disp->loport, disp->hiport, imp->msun.preresp->server_ip)) == 0) {
  1744.                         TRACE("bindPort failed\n");
  1745.                         MsgDestroyHandle(sconnhp);
  1746.                         disp->svcsHP = NULL;
  1747.                         ni_errno = NIE_NOBIND;                  /* can't bind a free application socket */
  1748.                         ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_ServiceRequest: <%s>", ni_errlist[ni_errno]);
  1749.                         return NULL;
  1750.                     }
  1751.                     TRACE("bindPort succeeded, port = %d\n", disp->clientPort);
  1752.                     if (NI_GETSOCKNAME(disp->svcsHP->sok, &svcsAddr, &sconnlen) >= 0)
  1753.                         disp->clientPort = ntohs(svcsAddr.sin_port);
  1754.                     req->clientPort = disp->clientPort;
  1755.                     svcreqp->server_ip = imp->msun.preresp->server_ip;
  1756.                     svcreqp->wantPreResponse = FALSE;
  1757.                     if (MsgWrite(disp->dispHP, mp, FALSE) < 0) {
  1758.                         MsgDestroyHandle(sconnhp);
  1759.                         ni_errno = NIE_MSGWRITE;
  1760.                         return NULL;
  1761.                     }
  1762.                     disp_contact = FALSE; /* now waiting to hear one more msg */
  1763.     
  1764.                     TRACE("After GETSOCKNAME, port = %d\n", disp->clientPort);
  1765.                     if ((status = NI_LISTEN(disp->svcsHP->sok, 5)) < 0) {
  1766. #ifdef NETP_INET_NEWT
  1767.                         SOCK_ERRNO = ABS(status);
  1768. #endif
  1769.                         TRACE("Listen failed, errno = %d\n", SOCK_ERRNO);
  1770.                         MsgDestroyHandle(sconnhp);
  1771.                         StringCpy(ni_errtext, sys_errlist[SOCK_INDEX_ERRNO]);
  1772.                         ni_errno = NIE_NOLISTEN;
  1773.                         ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_ServiceRequest: <%s> <port %d, errno %d>", ni_errlist[ni_errno], (int) disp->clientPort, (int) SOCK_ERRNO);
  1774.                         return NULL;
  1775.                     }
  1776.                 }
  1777. #endif
  1778.                 /* non-SOCKS clients ignore this message, and should never receive it */
  1779.                 TRACE("Listen succeeded\n");
  1780.                 MsgDestroy(imp);
  1781.                 break;
  1782.  
  1783.               case NI_SVC_RESPONSE:
  1784.                 if (disp->useSocks)
  1785.                 {
  1786.                     TRACE("Got SOCKS service response from Dispatcher\n");
  1787.                 }
  1788.                 ni_errno = NIE_NO_ERROR;
  1789.                 NI_DestroyRequest(disp->reqResponse);
  1790.                 disp->reqResponse = imp->msun.svcresp->request;
  1791.                 sconnhp->hostname = StringSave(disp->reqResponse->clientAddr);
  1792.                 imp->msun.svcresp->request = NULL;
  1793.                 MsgDestroy(imp);
  1794.                 break;
  1795.  
  1796.               case NI_NACK:
  1797.                 if (disp->useSocks)
  1798.                 {
  1799.                     TRACE("Got SOCKS NACK from Dispatcher\n");
  1800.                 }
  1801.                 ni_errno = (enum ni_error) imp->msun.nack->code;
  1802.                 if (imp->msun.nack->reason != NULL)
  1803.                     StringCpy(ni_errtext, imp->msun.nack->reason);
  1804.                 else
  1805.                     ni_errtext[0] = '\0';
  1806.                 MsgDestroy(imp);
  1807.                 MsgDestroyHandle(sconnhp);
  1808.                 return NULL;
  1809.  
  1810.               default:
  1811.                 if (disp->useSocks)
  1812.                 {
  1813.                     TRACE("Got SOCKS unknown message type from Dispatcher\n");
  1814.                 }
  1815.                 MsgDestroy(imp);
  1816.                 MsgDestroyHandle(sconnhp);
  1817.                 ni_errno = NIE_MSGUNK;          /* Unknown MSG type */
  1818.                 sprintf(ni_errtext, "%d", imp->type);
  1819.                 return NULL;
  1820.             }
  1821.         }
  1822.         if ((FD_ISSET(disp->svcsHP->sok, &readfds) != 0) && !serv_contact) {
  1823.             sconnlen = sizeof(sconnaddr);
  1824. #ifdef NETP_INET_NEWT
  1825.             sconnhp->sok = NI_ACCEPT(disp->svcsHP->sok, &sconnaddr, &sconnlen);
  1826. #else
  1827.             sconnhp->sok = NI_ACCEPT(disp->svcsHP->sok, (struct sockaddr PNTR) &sconnaddr, &sconnlen);
  1828. #endif /* NETP_INET_NEWT */
  1829.             LOG_SOCKET(sconnhp->sok, TRUE);
  1830. #ifdef NETP_SOCKS
  1831.             if (disp->useSocks)
  1832.             {
  1833.                 TRACE("Got connection from server, socket %d\n", sconnhp->sok);
  1834.                 MsgDestroyHandle(disp->svcsHP);
  1835.                 disp->svcsHP = MsgMakeHandle(TRUE); /* for next time */
  1836.             }
  1837. #endif /* NETP_SOCKS */
  1838.             if (sconnhp->sok < 0) {
  1839. #ifdef NETP_INET_NEWT
  1840.                 SOCK_ERRNO = ABS(sconnhp->sok);
  1841. #endif
  1842.                 MsgDestroyHandle(sconnhp);
  1843.                 StringCpy(ni_errtext, sys_errlist[SOCK_INDEX_ERRNO]);
  1844.                 ni_errno = NIE_NOACCEPT;        /* application accept error */
  1845.                 return NULL;
  1846.             }
  1847.             serv_contact = TRUE;
  1848.         }
  1849.     }
  1850.  
  1851.     if (activityHook != NULL)
  1852.     {
  1853.         activityHook(sconnhp, NetServHook_svcreq, 0);
  1854.     }
  1855.  
  1856.     if (disp->encryptInfo != NULL && NI_EncrAvailable())
  1857.     {
  1858.         NI_SetupDESEncryption(sconnhp, desKey);
  1859.     }
  1860.     return sconnhp;
  1861. } /* NI_ServiceRequest */
  1862.  
  1863.  
  1864.  
  1865. /*
  1866.  * Purpose:     Disconnect from a service provider
  1867.  *
  1868.  * Parameters:
  1869.  *   mhp          Message handle for the server
  1870.  *
  1871.  * Returns:
  1872.  *               0, always
  1873.  *
  1874.  *
  1875.  * Description:
  1876.  *              Disconnect from a service provider, essentially by just
  1877.  *              closing the communication socket to that service provider.
  1878.  */
  1879.  
  1880. Int2
  1881. NI_ServiceDisconnect(NI_HandPtr mhp)
  1882. {
  1883.     if (activityHook != NULL)
  1884.     {
  1885.         activityHook(mhp, NetServHook_svcdisconn, 0);
  1886.     }
  1887.  
  1888.     MsgDestroyHandle(mhp);
  1889.     return 0;
  1890. } /* NI_ServiceDisconnect */
  1891.  
  1892.  
  1893.  
  1894. /*
  1895.  * Purpose:     Obtain the read file descriptor from a "message handle"
  1896.  *
  1897.  * Parameters:
  1898.  *   handp        Message handle
  1899.  *
  1900.  * Returns:
  1901.  *               Socket associated with message handle
  1902.  *
  1903.  *
  1904.  * Description:
  1905.  *              Get the read file desciptor from a message handle. This
  1906.  *              might be useful, for example, when wishing to perform
  1907.  *              "direct" I/O to the socket after a connection has been
  1908.  *              established with a server/client.
  1909.  */
  1910.  
  1911. int
  1912. NI_ServiceGetReadFd(NI_HandPtr handp)
  1913. {
  1914.     return handp->sok;
  1915. } /* NI_ServiceGetReadFd */
  1916.  
  1917.  
  1918.  
  1919. /*
  1920.  * Purpose:     Obtain the write file descriptor from a "message handle"
  1921.  *
  1922.  * Parameters:
  1923.  *   handp        Message handle
  1924.  *
  1925.  * Returns:
  1926.  *               Socket associated with message handle
  1927.  *
  1928.  *
  1929.  * Description:
  1930.  *              Get the write file desciptor from a message handle. This
  1931.  *              might be useful, for example, when wishing to perform
  1932.  *              "direct" I/O to the socket after a connection has been
  1933.  *              established with a server/client.
  1934.  */
  1935.  
  1936. int
  1937. NI_ServiceGetWriteFd(NI_HandPtr handp)
  1938. {
  1939.     return handp->sok;
  1940. } /* NI_ServiceGetWriteFd */
  1941.  
  1942.  
  1943.  
  1944. /*
  1945.  * Purpose:     Populate a service request with a service name and version #s
  1946.  *
  1947.  * Parameters:
  1948.  *   req          Service request
  1949.  *   name         Service name
  1950.  *   vermin       Minimum version number for this service
  1951.  *   vermax       Maximum version number for this service
  1952.  *
  1953.  * Returns:
  1954.  *               -1, if the name is a NULL pointer
  1955.  *               0, otherwise
  1956.  *
  1957.  *
  1958.  * Description:
  1959.  *              Populate the service request with the specified service name
  1960.  *              and version numbers, dynamically allocating space for the
  1961.  *              service name.
  1962.  */
  1963.  
  1964. Int2
  1965. NI_RequestSetService(NI_ReqPtr req, CharPtr name, Uint2 vermin, Uint2 vermax)
  1966. {
  1967.     if (name == NULL) {
  1968.         ni_errno = NIE_INVAL;
  1969.         return -1;
  1970.     }
  1971.     req->service->name = StringSave(name);
  1972.     req->service->minVersion = vermin;
  1973.     req->service->maxVersion = vermax;
  1974.     req->service->typeL = NULL;
  1975.     return 0;
  1976. } /* NI_RequestSetService */
  1977.  
  1978.  
  1979.  
  1980. /*
  1981.  * Purpose:     Populate a service request with an additional resource
  1982.  *
  1983.  * Parameters:
  1984.  *   req          Service request
  1985.  *   name         Resource name
  1986.  *   type         Service type
  1987.  *   vermin       Minimum version number for this resource
  1988.  *   vermax       Maximum version number for this resource
  1989.  *
  1990.  * Returns:
  1991.  *               -1, if the name is a NULL pointer
  1992.  *               0, otherwise
  1993.  *
  1994.  *
  1995.  * Description:
  1996.  *              Insert the information for this resource into a list of
  1997.  *              resources associated with this service request. This
  1998.  *              function may be called one or more times (or, not at all) to
  1999.  *              populate a service request with one or more resources.
  2000.  */
  2001.  
  2002. Int2
  2003. NI_RequestAddResource(NI_ReqPtr req, CharPtr name, CharPtr type, Uint2 vermin, Uint2 vermax)
  2004.  
  2005. {
  2006.     NIResPtr    resp;
  2007.  
  2008.     if (name == NULL) {
  2009.         ni_errno = NIE_INVAL;
  2010.         return -1;
  2011.     }
  2012.     resp = NI_MakeResource();
  2013.     resp->name = StringSave(name);
  2014.     if (type != NULL)
  2015.         resp->type = StringSave(type);
  2016.     resp->minVersion = vermin;
  2017.     resp->maxVersion = vermax;
  2018.     req->resourceL = ListInsertPrev((VoidPtr) resp, req->resourceL);    /* add to end of list */
  2019.     return 0;
  2020. } /* NI_RequestAddResource */
  2021.  
  2022.  
  2023.  
  2024. /* THESE FUNCTIONS NOT VISIBLE TO API USER */
  2025.  
  2026. /*
  2027.  * Purpose:     Partially halt Network Services
  2028.  *
  2029.  * Parameters:
  2030.  *   disp         A pointer to the dispatcher structure
  2031.  *
  2032.  * Description:
  2033.  *              Halt network services, except refrain from freeing the
  2034.  *              parameters which are set by NI_SetDispatcher().
  2035.  */
  2036.  
  2037. static void
  2038. HaltServices (NI_DispatcherPtr disp)
  2039. {
  2040.     if (disp == NULL)
  2041.         return;
  2042.  
  2043.     if (disp->referenceCount > 0)
  2044.         return;
  2045.  
  2046.     if (activityHook != NULL)
  2047.     {
  2048.         activityHook((NI_HandPtr) disp, NetServHook_dispdisconn, 0);
  2049.     }
  2050.  
  2051.     MsgDestroyHandle(disp->dispHP);
  2052.     MsgDestroyHandle(disp->svcsHP);
  2053.     NI_DestroyRequest(disp->reqResponse);
  2054.     if (disp->identity != NULL) {
  2055.         MemFree (disp->identity->username);
  2056.         MemFree (disp->identity->group);
  2057.         MemFree (disp->identity->domain);
  2058.         MemFree (disp->identity);
  2059.         disp->identity = NULL;
  2060.     }
  2061.     disp->dispHP = NULL;
  2062.     disp->svcsHP = NULL;
  2063.     disp->reqResponse = NULL;
  2064.     if (disp->encryptInfo != NULL)
  2065.     {
  2066.         if (disp->encryptInfo != NULL)
  2067.             NI_DestroyPubKey((NIPubKeyPtr) disp->encryptInfo->data.ptrvalue);
  2068.         ValNodeFree(disp->encryptInfo);
  2069.     }
  2070.  
  2071. #ifdef NETP_INET_WSOCK
  2072.     /* we have an obligation to perform one cleanup call for every Startup */
  2073.     while (wsaStartupCount-- > 0)
  2074.     {
  2075.         WSACleanup();
  2076.     }
  2077. #endif
  2078. }
  2079.  
  2080.  
  2081. /*
  2082.  * Purpose:     Lookup a port # in config file and possible NIS
  2083.  *
  2084.  * Parameters:
  2085.  *   service      Name of config. file entry
  2086.  *   networkOrder Boolean, indicates whether value should be returned in host
  2087.  *                order or network order.
  2088.  *
  2089.  * Description:
  2090.  *              Look up the specified entry in the NCBI config. file, and
  2091.  *              lookup in NIS the name obtained from the config file if it's
  2092.  *              non-numeric.
  2093.  *
  2094.  * Note:
  2095.  *              The intent of this function is that, in most cases, the
  2096.  *              GetAppParam() entry will not be present, and a default value
  2097.  *              will be used instead.  The getservbyname() call is intended
  2098.  *              to be a last resort, because this may be slow on some systems.
  2099.  */
  2100.  
  2101. static Uint2
  2102. GetByConfigOrServ(CharPtr service, Boolean networkOrder)
  2103. {
  2104.     struct servent PNTR portEntry;
  2105.     Char                buf[50];
  2106.     Uint2               port;
  2107.  
  2108.     if (Nlm_GetAppParam("NCBI", "NET_SERV", service, "", buf, sizeof buf) <= 0)
  2109.     {
  2110.         port = 0;
  2111.     } else {
  2112.         if (StrSpn(buf, "0123456789") == StrLen(buf))
  2113.         { /* all numeric */
  2114.             port = atoi(buf);
  2115.             if (networkOrder)
  2116.                port = htons(port);
  2117.         } else {
  2118.             /* entry from configuration file is name to use in getservbyname */
  2119.             if ((portEntry = getservbyname(buf, "tcp")) == NULL)
  2120.             {
  2121.                 port = 0;
  2122.             } else  {
  2123.                 port = portEntry->s_port;
  2124.                 if (! networkOrder)
  2125.                     port = ntohs(port);
  2126.             }
  2127.         }
  2128.     }
  2129.  
  2130.     return port;
  2131. }
  2132.  
  2133.  
  2134. /*
  2135.  * Purpose:     Connect to the dispatcher
  2136.  *
  2137.  * Parameters:
  2138.  *   disp         A pointer to the dispatcher structure
  2139.  *   host         Name of the host on which dispatcher resides
  2140.  *   service      Name of the "service" (i.e., port) to which we should connect
  2141.  *   timeout      How long to wait for dispatcher to respond, 0 ==> use default
  2142.  *
  2143.  * Returns:
  2144.  *               NULL, if the attempt to connect failed
  2145.  *               a pointer to the "Msg" structure for the dispatcher, otherwise
  2146.  *
  2147.  *
  2148.  * Description:
  2149.  *               Connect to the dispatcher on the specified hostname on the
  2150.  *               specified service (where a service maps to a port number).
  2151.  *               This is done by establishing a socket to the dispatcher,
  2152.  *               and then connect()ing to that socket; the dispatcher should
  2153.  *               be listen()ing on that socket, and should subsequently accept()
  2154.  *               the connection request.
  2155.  *
  2156.  *               While doing this, also obtain other useful information;
  2157.  *               namely, the dotted IP address of the local host, and the
  2158.  *               high and low port numbers to be used when attempting
  2159.  *               dispatcher connections. This global information is used
  2160.  *               elsewhere.
  2161.  */
  2162.  
  2163. #ifndef INADDR_NONE
  2164. #define INADDR_NONE             -1
  2165. #endif /* INADDR_NONE */
  2166.  
  2167. static NI_HandPtr
  2168. DispatchConnect(NI_DispatcherPtr disp, CharPtr host, CharPtr service, int timeout)
  2169. {
  2170.     struct hostent      PNTR dispHost, PNTR localHost;
  2171.     struct sockaddr_in  serv_addr;
  2172.     NI_HandPtr          dHP;
  2173.     Uint2               disp_port;
  2174.     Uint4               srvadd;
  2175.     Char                servInetAddr[INETADDR_SIZ], localHostName[SVC_HOST_SIZ];
  2176.     Char                t_service[64];
  2177.     int                 status;
  2178.     Int4                connectStartTime;
  2179.  
  2180.     if (disp == NULL)
  2181.         return NULL;
  2182.  
  2183.  
  2184.     serv_addr.sin_family = AF_INET;
  2185.  
  2186.     srvadd = inet_addr(host);
  2187.     if ((Int4)srvadd != INADDR_NONE)    /* malformed request */
  2188.         MemCopy((VoidPtr) &serv_addr.sin_addr, (VoidPtr) &srvadd, sizeof(srvadd));
  2189.     else {
  2190.         if ((dispHost = gethostbyname(host)) == NULL) {
  2191.             ni_errno = NIE_NOHOSTENT;
  2192.             return NULL;
  2193.         }
  2194. /*      MemCopy((VoidPtr)&serv_addr.sin_addr, (VoidPtr)(dispHost->h_addr), dispHost->h_length);*/
  2195.         MemCopy(&serv_addr.sin_addr, dispHost->h_addr, dispHost->h_length);
  2196.     }
  2197.     StringCpy(servInetAddr, inet_ntoa(serv_addr.SIN_ADDR));
  2198.  
  2199.     if ((disp_port = GetByConfigOrServ(service, TRUE)) == 0)
  2200.     {
  2201.         if (service)
  2202.             StringCpy(t_service, service);      /* because Windows barfs on the pointer */
  2203.         else
  2204.             t_service[0] = 0;
  2205.         if ((disp_port = htons(atoi(t_service))) == 0)
  2206.             disp_port = htons(NI_DFLT_SVC_PORT);
  2207.     }
  2208.     if (ntohs(disp_port) <= NI_LAST_RESERVED_PORT) {
  2209.         ni_errno = NIE_NOSERVENT;
  2210.         return NULL;
  2211.     }
  2212.  
  2213.     /* get the Internet address of the "local host" */
  2214. #ifdef NETP_INET_MACTCP
  2215.     /* simpler solution to avoid the hazards of gethostname() */
  2216.     {
  2217.         unsigned long localHostId;
  2218.  
  2219.         localHostId = gethostid();
  2220.         StringCpy(disp->localHostAddr, inet_ntoa(* (H_ADDR_TYPE) &localHostId));
  2221.     }
  2222. #else
  2223.     gethostname(localHostName, SVC_HOST_SIZ);
  2224.     if ((localHost = gethostbyname(localHostName)) == NULL) {
  2225.         /* Nlm_Nlm_GetAppParam() workaround for PC-NFS 5.0 bug */
  2226.         if (GetAppParam("NCBI", "NET_SERV", "HOST_ADDRESS", "",
  2227.                         disp->localHostAddr, sizeof(disp->localHostAddr)) <= 0)
  2228.         { /* use a bogus address which the dispatcher will try to fix */
  2229.             StringCpy(disp->localHostAddr, "0.0.0.0");
  2230.  
  2231.         }
  2232.     } else {
  2233.         StringCpy(disp->localHostAddr, inet_ntoa(* (H_ADDR_TYPE) localHost->h_addr));
  2234.     }
  2235. #endif /* NETP_INET_MACTCP */
  2236.  
  2237.     if ((disp->loport = GetByConfigOrServ(NI_CLIENT_PORT_LO_NAME, FALSE)) == 0)
  2238.     {
  2239.         if ((disp->loport = atoi(NI_CLIENT_PORT_LO_NAME)) == 0)
  2240.             disp->loport = NI_DFLT_CLILO_PORT;
  2241.     }
  2242.     if (disp->loport <= NI_LAST_RESERVED_PORT) {
  2243.         ni_errno = NIE_BADPORT;         /* bad low client port */
  2244.         return NULL;
  2245.     }
  2246.  
  2247.     if ((disp->hiport = GetByConfigOrServ(NI_CLIENT_PORT_HI_NAME, FALSE)) == 0)
  2248.     {
  2249.         if ((disp->hiport = atoi(NI_CLIENT_PORT_HI_NAME)) == 0)
  2250.             disp->hiport = NI_DFLT_CLIHI_PORT;
  2251.     }
  2252.     if (disp->hiport <= NI_LAST_RESERVED_PORT) {
  2253.         ni_errno = NIE_BADPORT;         /* bad high client port */
  2254.         return NULL;
  2255.     }
  2256.  
  2257.     MemFill((VoidPtr) &serv_addr, '\0', sizeof(serv_addr));
  2258.     serv_addr.sin_family = AF_INET;
  2259.     serv_addr.sin_addr.s_addr = inet_addr(servInetAddr);
  2260.     serv_addr.sin_port = disp_port;
  2261.  
  2262.     if ((dHP = MsgMakeHandle(TRUE)) == NULL)
  2263.         return NULL;
  2264.     MsgSetLJError(dHP);
  2265.     if (timeout > 0)
  2266.         MsgSetReadTimeout(dHP, timeout);
  2267.  
  2268.     if (activityHook != NULL)
  2269.     {
  2270.         activityHook((NI_HandPtr) disp, NetServHook_dispconn, 0);
  2271.     }
  2272.  
  2273. #ifdef NETP_SOCKS
  2274.     if (disp->useSocks)
  2275.     { /* SOCKS can't deal well with blocking connections */
  2276.         NI_SETBLOCKING(dHP->sok);
  2277.     }
  2278. #endif
  2279.  
  2280.     connectStartTime = Nlm_GetSecs();
  2281.  
  2282.   RETRY:
  2283. #ifndef NETP_INET_NEWT
  2284.     if ((status = NI_CONNECT(dHP->sok, (struct sockaddr PNTR) &serv_addr, sizeof(serv_addr))) < 0) { /* } */
  2285. #else
  2286.     if ((status = NI_CONNECT(dHP->sok, &serv_addr, sizeof(serv_addr))) < 0) {
  2287.         SOCK_ERRNO = ABS(status);
  2288. #endif
  2289.         switch (SOCK_ERRNO) {
  2290.           case EINTR:
  2291.             goto RETRY;
  2292.  
  2293. #ifdef NETP_INET_PCNFS
  2294.           /* This is apparently a bug in PC-NFS 4.0 ... a connection attempt */
  2295.           /* on a non-blocking socket yields errno == 0                      */
  2296.           case 0:
  2297. #endif /* NETP_INET_PCNFS */
  2298.           case EWOULDBLOCK:
  2299.           case EINPROGRESS:
  2300.             /* if the connect()ion is not established immediately, a         */
  2301.             /* select() can be performed where the corresponding "write"     */
  2302.             /* file descriptor will be enabled once the connect()ion has been*/
  2303.             /* established                                                   */
  2304.             status = sizeof(serv_addr);
  2305.             if (sokselectw(dHP->sok, timeout) == 0
  2306. #ifdef OS_UNIX
  2307.                 && getpeername(dHP->sok, &serv_addr, &status) == 0
  2308. #endif
  2309.                 ) {
  2310.                 dHP->state = NI_CONNECTED;
  2311.                 dHP->connectDelay = Nlm_GetSecs() - connectStartTime;
  2312.                 return dHP;
  2313.             }
  2314.             break;
  2315.  
  2316.           default:
  2317.             break;
  2318.         }
  2319.         MsgDestroyHandle(dHP);
  2320.         ni_errno = NIE_DISPCONN;        /* can't connect to dispatcher */
  2321.         return NULL;
  2322.     }
  2323.     dHP->state = NI_CONNECTED;
  2324.     dHP->connectDelay = Nlm_GetSecs() - connectStartTime;
  2325.     return dHP;
  2326. } /* DispatchConnect */
  2327.  
  2328.  
  2329. /*
  2330.  * Purpose:     Get the platform on which this client is running
  2331.  *
  2332.  * Parameters:
  2333.  *                none
  2334.  *
  2335.  * Returns:
  2336.  *               the client's platform, or NI_PLATFORM_UNKNOWN
  2337.  *
  2338.  *
  2339.  * Description:
  2340.  *               Calculate what platform this client is running on.
  2341.  *
  2342.  *
  2343.  * Note:
  2344.  *               Although the initial implementation of this function
  2345.  *               calculates the platform-type at compile-time, it is
  2346.  *               legitimate to perform some computation at run time, e.g.,
  2347.  *               to determine whether this client is using a particular
  2348.  *               low-level driver.
  2349.  *
  2350.  *               The dispatcher and servers should not rely on the
  2351.  *               information which is received for platform-type, because
  2352.  *               the client may be lying, either because of a coding error
  2353.  *               or malice on the part of a client developer.
  2354.  */
  2355.  
  2356. Int2
  2357. NI_GetPlatform (void)
  2358. {
  2359.     static Boolean alreadyInited = FALSE;
  2360.     static Int2 retval;
  2361.  
  2362.     if (alreadyInited)
  2363.     {
  2364.       return retval;
  2365.     }
  2366.  
  2367.     alreadyInited = TRUE;
  2368.  
  2369.     retval = NI_PLATFORM_UNKNOWN;
  2370.  
  2371. #ifdef NETP_INET_MACTCP
  2372.     retval = NI_PLATFORM_MAC;
  2373. #endif
  2374.  
  2375. #ifdef OS_VMS
  2376. #ifdef NETP_INET_TGV
  2377.     retval = NI_PLATFORM_VMS_TGV;
  2378. #endif
  2379. #ifdef NETP_INET_TWG
  2380.     retval = NI_PLATFORM_VMS_TWG;
  2381. #endif
  2382. #ifdef NETP_INET_WPW
  2383.     retval = NI_PLATFORM_VMS_WPW;
  2384. #endif
  2385. #ifdef NETP_INET_UCX
  2386.     retval = NI_PLATFORM_VMS_UCX;
  2387. #endif
  2388. #ifdef OS_AXP_VMS
  2389.     retval = NI_PLATFORM_AXP_OPENVMS;
  2390. #endif
  2391. #endif /* OS_VMS */
  2392.  
  2393. #ifdef OS_UNIX
  2394.     retval = NI_PLATFORM_GENERIC_UNIX;
  2395. #ifdef PROC_IBM370
  2396.     retval = NI_PLATFORM_IBM370AIX;
  2397. #endif
  2398. #ifdef OS_UNIX_SUN
  2399.     retval = NI_PLATFORM_SUN;
  2400. #endif
  2401. #if defined(OS_UNIX_OSF1) && defined(PROC_ALPHA)
  2402.     retval = NI_PLATFORM_ALPHA_OSF1;
  2403. #endif
  2404. #ifdef COMP_AUX
  2405.     retval = NI_PLATFORM_AUX;
  2406. #endif
  2407. #if defined(COMP_CRAY) && defined(PROC_YMP)
  2408.     retval = NI_PLATFORM_CRAY;
  2409. #endif
  2410. #ifdef PROC_CONVEX
  2411.     retval = NI_PLATFORM_CONVEX;
  2412. #endif
  2413. #ifdef PROC_HPPA
  2414.     retval = NI_PLATFORM_HPUX;
  2415. #endif
  2416. #ifdef OS_UNIX_NEXT
  2417.     retval = NI_PLATFORM_NEXT;
  2418. #endif
  2419. #ifdef PROC_MIPS
  2420.     retval = NI_PLATFORM_SGI;
  2421. #endif
  2422. #ifdef OS_UNIX_ULTRIX
  2423.     retval = NI_PLATFORM_ULTRIX;
  2424. #endif
  2425. #if defined(OS_UNIX_SYSV) && defined(PROC_SPARC)
  2426.     retval = NI_PLATFORM_SYSV_ON_SPARC;
  2427. #endif
  2428. #ifdef OS_UNIX_AIX
  2429.     retval = NI_PLATFORM_AIX;
  2430. #endif
  2431. #ifdef OS_UNIX_LINUX
  2432.     retval = NI_PLATFORM_LINUX;
  2433. #endif
  2434. #endif /* OS_UNIX */
  2435.  
  2436. #ifdef OS_DOS
  2437.     retval = NI_PLATFORM_DOS;
  2438. #ifdef WIN16
  2439.     retval = NI_PLATFORM_WIN16;
  2440. #endif
  2441. #ifdef NETP_INET_NEWT
  2442.     retval = NI_PLATFORM_WIN_NEWT;
  2443. #endif
  2444. #ifdef NETP_INET_PCNFS
  2445.     retval = NI_PLATFORM_WIN_PCNFS;
  2446. #endif
  2447. #ifdef WINSOCK
  2448.     retval = NI_PLATFORM_WIN_WINSOCK;
  2449. #endif
  2450. #endif /* OS_DOS */
  2451.  
  2452. #ifdef OS_WINNT
  2453.     retval = NI_PLATFORM_WINNT;
  2454. #endif
  2455.  
  2456.     return retval;
  2457. }
  2458.  
  2459.  
  2460. /*
  2461.  * Purpose:     Set the "identity" of this client
  2462.  *
  2463.  * Parameters:
  2464.  *   disp         A pointer to the dispatcher structure
  2465.  *   user         New Username
  2466.  *   group        New Groupname
  2467.  *   domain       New DomainName
  2468.  *
  2469.  * Returns:
  2470.  *               0, always
  2471.  *
  2472.  *
  2473.  * Description:
  2474.  *               Allocate the space for the "UID" structure, if not already
  2475.  *               allocated, and populate it with the user name, group name,
  2476.  *               and domain name.
  2477.  */
  2478.  
  2479. static Int2
  2480. SetIdentity(NI_DispatcherPtr disp, CharPtr user, CharPtr group, CharPtr domain)
  2481. {
  2482.     if (disp == NULL)
  2483.         return 0;
  2484.  
  2485.     if (disp->identity == NULL)
  2486.         disp->identity = NI_MakeUid();
  2487.  
  2488.     if (disp->identity->username != NULL)
  2489.         MemFree(disp->identity->username);
  2490.     disp->identity->username = StringSave(user);
  2491.     if (disp->identity->group != NULL)
  2492.         MemFree(disp->identity->group);
  2493.     if (group != NULL)
  2494.         disp->identity->group = StringSave(group);
  2495.     else
  2496.         disp->identity->group = NULL;
  2497.     if (disp->identity->domain != NULL)
  2498.         MemFree(disp->identity->domain);
  2499.     disp->identity->domain = StringSave(domain);
  2500.     return 0;
  2501. } /* SetIdentity */
  2502.  
  2503.  
  2504.  
  2505. /*
  2506.  * Purpose:     Copy from the "identity" UID to the specified UID data struct
  2507.  *
  2508.  * Parameters:
  2509.  *   disp         A pointer to the dispatcher structure
  2510.  *   uid          UID structure to be copied into
  2511.  *
  2512.  * Returns:
  2513.  *               -1, if invalid arguments
  2514.  *               0, otherwise
  2515.  *
  2516.  *
  2517.  * Description:
  2518.  *              Copy fields from the "identity" UID data structure into the
  2519.  *              UID data structure provided by the caller.
  2520.  */
  2521.  
  2522. static Int2
  2523. CopyIdentity(NI_DispatcherPtr disp, NI_UidPtr uid)
  2524. {
  2525.     if (disp == NULL || disp->identity == NULL || uid == NULL)
  2526.         return -1;
  2527.     if  (uid->username != NULL)
  2528.         MemFree(uid->username);
  2529.     uid->username = StringSave(disp->identity->username);
  2530.     if  (uid->group != NULL)
  2531.         MemFree(uid->group);
  2532.     uid->group = StringSave(disp->identity->group);
  2533.     if  (uid->domain != NULL)
  2534.         MemFree(uid->domain);
  2535.     uid->domain = StringSave(disp->identity->domain);
  2536.     return 0;
  2537. } /* CopyIdentity */
  2538.  
  2539.  
  2540.  
  2541. /*
  2542.  * Purpose:     Select the next available port within the given range,
  2543.  *              and bind a socket to it.
  2544.  *
  2545.  * Parameters:
  2546.  *   sok          Socket to be bound to a port (INPUT)
  2547.  *   sokadr       Socket data structure to be populated (OUTPUT)
  2548.  *   loport       Minimum acceptable port number
  2549.  *   hiport       Maximum acceptable port number
  2550.  *
  2551.  * Returns:
  2552.  *               0, if unable to bind to a port
  2553.  *               the selected ("bound") port number, otherwise
  2554.  *
  2555.  *
  2556.  * Description:
  2557.  *              Iterate through the range of acceptable port numbers, until
  2558.  *              an unused port number can be selected to which the socket
  2559.  *              can be bound.
  2560.  */
  2561.  
  2562. static Uint2
  2563. bindPort(int sok, struct sockaddr_in PNTR sokadr, Int2 loport, Int2 hiport, Uint4 remoteHost)
  2564. {
  2565.     int                 status;
  2566. #ifdef NETP_INET_MACTCP
  2567.     int                 delta = 0;
  2568.     Char                buf[20];
  2569. #endif
  2570.  
  2571.     if (hiport == 0)
  2572.         hiport = loport;
  2573.     if (loport > hiport)
  2574.         return 0;
  2575.  
  2576. #ifdef NETP_INET_MACTCP
  2577.     /* use a hint from the configuration file to avoid port # conflicts */
  2578.     if (hiport > loport && Nlm_GetAppParam("NCBI", "NET_SERV", "PORT_DELTA", "0",
  2579.         buf, sizeof buf) > 0)
  2580.     {
  2581.         delta = atoi(buf);
  2582.         loport += delta % (hiport - loport);
  2583.         sprintf (buf, "%d", delta + 1);
  2584.         SetAppParam("NCBI", "NET_SERV", "PORT_DELTA", buf);
  2585.     }
  2586. #endif
  2587.  
  2588.     MemFill((VoidPtr) sokadr, '\0', sizeof(struct sockaddr_in));
  2589.     sokadr->sin_family = AF_INET;
  2590.     sokadr->sin_addr.s_addr = INADDR_ANY;
  2591.  
  2592.     while (loport <= hiport) {
  2593.         sokadr->sin_port = htons(loport);
  2594. #ifdef NETP_INET_NEWT
  2595.         if ((status = NI_BIND(sok, sokadr, sizeof(struct sockaddr_in), htonl(remoteHost))) == 0)
  2596. #else
  2597.         if ((status = NI_BIND(sok, (struct sockaddr PNTR) sokadr, sizeof(struct sockaddr_in), htonl(remoteHost))) == 0)
  2598. #endif /* NETP_INET_NEWT */
  2599.             return (Uint2) ntohs(sokadr->sin_port);
  2600.         else {
  2601. #ifdef NETP_INET_NEWT
  2602.             SOCK_ERRNO = ABS(status);
  2603. #endif
  2604.             loport++;
  2605.         }
  2606.     }
  2607.     return 0;
  2608. } /* bindPort */
  2609.  
  2610.  
  2611.  
  2612. /* SERVER FUNCTIONS */
  2613.  
  2614. static int      writepipe PROTO((int fd, char *buf, int len));
  2615.  
  2616. /*
  2617.  * Purpose:     Write a message on the pipe from a child server application
  2618.  *              process to its parent NCBID.
  2619.  *
  2620.  * Parameters:
  2621.  *   fd           Pipe file descriptor
  2622.  *   buf          Buffer to be written
  2623.  *   len          Length of buffer
  2624.  *
  2625.  * Returns:
  2626.  *                0, if unable to write because the pipe is full
  2627.  *                number of bytes written, otherwise
  2628.  *
  2629.  *
  2630.  * Description:
  2631.  *              Write the specified number of bytes to a pipe, and handle
  2632.  *              multiple write attempts if necessary, to handle the case where
  2633.  *              a write() may be interrupted by a signal.
  2634.  *
  2635.  * Note:
  2636.  *              This routine is only used by a child process after it has been
  2637.  *              forked and before it has been execed.
  2638.  */
  2639.  
  2640. static int
  2641. writepipe(int fd, char *buf, int len)
  2642. {
  2643.     int         byteswrit;
  2644.  
  2645.   WriteAgain:
  2646.     if ((byteswrit = write(fd, buf, len)) < 0) {
  2647.         switch (errno) {
  2648.           case EINTR:
  2649.             goto WriteAgain;
  2650.  
  2651.           case EWOULDBLOCK:
  2652.           default:
  2653.             return 0;
  2654.         }
  2655.     }
  2656.  
  2657.     return byteswrit;
  2658. } /* writepipe */
  2659.  
  2660. static Int2 StandAlonePort(void)
  2661. {
  2662.     CharPtr env;
  2663.  
  2664. #ifdef OS_UNIX
  2665.     if ((env = getenv("NI_STANDALONE_SERVER")) == NULL)
  2666.     {
  2667.         return 0;
  2668.     }
  2669.  
  2670.     return atoi(env);
  2671. #else
  2672.     return 0;
  2673. #endif
  2674. }
  2675.  
  2676.  
  2677. /*
  2678.  * Purpose:     Send an "ACK" from a child server application process to its
  2679.  *              parent NCBID.
  2680.  *
  2681.  * Returns:
  2682.  *                0, if the ACK was sent successfully
  2683.  *                -1, otherwise
  2684.  *
  2685.  *
  2686.  * Description:
  2687.  *              Write an "ACK" from a child server application process to its
  2688.  *              parent NCBID, on the pipe connecting the two processes.
  2689.  *
  2690.  * Note:
  2691.  *              This routine should be called by a child process after it has
  2692.  *              determined that it has started successfully. At most one
  2693.  *              of NI_ServerACK() and NI_ServerNACK() may be called.
  2694.  */
  2695.  
  2696. #define TEMP_BUF_SIZ    256
  2697.  
  2698. int
  2699. NI_ServerACK(void)
  2700. {
  2701.     int         wstat;
  2702.     Char        temp_buf[TEMP_BUF_SIZ];
  2703.     Int2        port;
  2704.  
  2705.     if ((port = StandAlonePort()) == 0)
  2706.     { /* not stand-alone */
  2707.         sprintf(temp_buf, PIPE_MSG_FMT, NIE_SERVACK, "OK");
  2708.         if ((wstat = writepipe(STDPIPE, temp_buf, strlen(temp_buf))) <= 0) {
  2709.             ni_errno = NIE_PIPEIO;
  2710.             strcpy(ni_errtext, (wstat == 0) ? "EWOULDBLOCK" : sys_errlist[errno]);
  2711.             return -1;
  2712.         }
  2713.     } else { /* stand-alone */
  2714. #ifdef OS_UNIX
  2715.         /* non-UNIX platforms currently experience compilation errors */
  2716.         struct sockaddr_in  soktAddr;
  2717.         NI_HandPtr hp;
  2718.         int sok;
  2719.         int status;
  2720.         struct sockaddr_in sockaddr;
  2721.         int soktLen;
  2722.         CharPtr security;
  2723.         int one = 1; /* for SO_REUSEADDR */
  2724.  
  2725.         hp = MsgMakeHandle(TRUE);
  2726.         NI_SETBLOCKING(hp->sok);
  2727.  
  2728.         MemFill(&sockaddr, '\0', sizeof(struct sockaddr_in));
  2729.         sockaddr.sin_family = AF_INET;
  2730.         sockaddr.sin_addr.s_addr = INADDR_ANY;
  2731.         sockaddr.sin_port = htons(port);
  2732.  
  2733.         if (setsockopt(hp->sok, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
  2734.                        sizeof(one)) < 0)
  2735.         {
  2736.             Message (MSG_ERROR, "Unable to set socket re-usability, errno = %d", errno);
  2737.         }
  2738.  
  2739. #ifdef NETP_INET_NEWT
  2740.         if ((status = bind(hp->sok, &sockaddr, sizeof(struct sockaddr_in))) == 0)
  2741. #else
  2742.         if ((status = bind(hp->sok, (struct sockaddr PNTR) &sockaddr, sizeof(struct sockaddr_in))) != 0)
  2743. #endif /* NETP_INET_NEWT */
  2744.         { /* error */
  2745.             ErrPostEx(SEV_FATAL,0,0, 
  2746.                       "Bind failed on socket %d, status = %d, errno = %d",
  2747.                       hp->sok, status, errno);
  2748.             return -1;
  2749.         }
  2750.  
  2751.         NI_LISTEN(hp->sok, 1);
  2752.         close(0); /* so that accept() will return 0 */
  2753.         soktLen = sizeof(soktAddr);
  2754.  
  2755.         /* accept the connection */
  2756.         if ((sok = NI_ACCEPT(hp->sok, (struct sockaddr *) &soktAddr, &soktLen)) < 0)
  2757.         { /* error */
  2758.             ErrPostEx(SEV_FATAL,0,0, "Accept returned bad file descriptor %d, errno = %d",
  2759.                     sok, errno);
  2760.             return -1;
  2761.         }
  2762.         LOG_SOCKET(sok, TRUE);
  2763.         MsgDestroyHandle(hp);
  2764.         if ((security = getenv("NI_STANDALONE_SECURITY")) != NULL)
  2765.         { /* security must be substring of client address */
  2766.             if (StrNCmp(inet_ntoa(soktAddr.SIN_ADDR), security, StrLen(security)) != 0)
  2767.             {
  2768.                 close(sok);
  2769.                 ErrPostEx(SEV_FATAL,0,0, "Security violation from IP address %s, security = %s\n",
  2770.                         inet_ntoa(soktAddr.SIN_ADDR), security);
  2771.                 return -1;
  2772.             }
  2773.         }
  2774. #endif /* OS_UNIX */
  2775.     }
  2776.     return 0;
  2777. } /* NI_ServerACK */
  2778.  
  2779.  
  2780.  
  2781. /*
  2782.  * Purpose:     Send an "NACK" from a child server application process to its
  2783.  *              parent NCBID.
  2784.  *
  2785.  * Returns:
  2786.  *                0, if the NACK was sent successfully
  2787.  *                -1, otherwise
  2788.  *
  2789.  *
  2790.  * Description:
  2791.  *              Write an "NACK" from a child server application process to its
  2792.  *              parent NCBID, on the pipe connecting the two processes.
  2793.  *
  2794.  * Note:
  2795.  *              This routine should be called by a child process after it has
  2796.  *              determined that it will be unable to start successfully. In
  2797.  *              the event that this routine is not called (or is unable to
  2798.  *              perform its function), a timeout mechanism must be relied
  2799.  *              upon for the NCBID to realize that a child has started
  2800.  *              unsuccessfully.
  2801.  *
  2802.  *              At most one of NI_ServerACK() and NI_ServerNACK() may be called.
  2803.  */
  2804.  
  2805. int
  2806. NI_ServerNACK(CharPtr err_text)
  2807. {
  2808.     int         wstat;
  2809.     Char        temp_buf[TEMP_BUF_SIZ];
  2810.  
  2811.     sprintf(temp_buf, PIPE_MSG_FMT, NIE_SERVNACK, err_text);
  2812.     if (StandAlonePort() == 0)
  2813.     { /* not stand-alone */
  2814.         if ((wstat = writepipe(STDPIPE, temp_buf, strlen(temp_buf))) <= 0) {
  2815.             ni_errno = NIE_PIPEIO;
  2816.             strcpy(ni_errtext, (wstat == 0) ? "EWOULDBLOCK" : sys_errlist[errno]);
  2817.             return -1;
  2818.         }
  2819.     } else { /* stand-alone */
  2820.         ErrPostEx(SEV_FATAL,0,0, "Stand-alone server failed startup {%s}", temp_buf);
  2821.         return -1;
  2822.     }
  2823.     return 0;
  2824. } /* NI_ServerNACK */
  2825.  
  2826.  
  2827.  
  2828. /*
  2829.  * Purpose:     Open the stream to be used for ASN I/O between a server
  2830.  *              application process and its client.
  2831.  *
  2832.  * Returns:
  2833.  *                NULL, if something went wrong
  2834.  *                a pointer to the Msg structure, otherwise
  2835.  *
  2836.  *
  2837.  * Description:
  2838.  *              Create a "Msg" structure for ASN I/O, and associate the Msg's
  2839.  *              socket with the standard input file descriptor (STDIN), which is
  2840.  *              the communication socket between the server application process
  2841.  *              and its client.
  2842.  *
  2843.  * Note:
  2844.  *              This routine should only be called by a child application
  2845.  *              process (not by a client).
  2846.  */
  2847.  
  2848. NI_HandPtr
  2849. NI_OpenASNIO(void)
  2850. {
  2851.     NI_HandPtr  hp;
  2852.  
  2853.     if ((hp = MsgMakeHandle(FALSE)) == NULL)
  2854.         return NULL;
  2855.  
  2856.     MsgSetReadTimeout(hp, NI_SERV_LISTEN_TIMEOUT);      /* set default for servers to listen */
  2857.  
  2858.     if ((hp->sok = dup(STDIN)) == -1) {         /* STDOUT points to same socket */
  2859.         MsgDestroyHandle(hp);
  2860.         return NULL;
  2861.     }
  2862.     LOG_SOCKET(hp->sok, TRUE);
  2863.     {
  2864.         CharPtr buf;
  2865.         Char key[8];
  2866.  
  2867.         if ((buf = getenv("NI_DESKEY")) != NULL &&
  2868.             AsnTypeStringToHex(buf, StrLen(buf), key))
  2869.         {
  2870.             NI_SetupDESEncryption(hp, (UcharPtr) key);
  2871.         }
  2872.     }
  2873.     return hp;
  2874. } /* NI_OpenASNIO */
  2875.  
  2876.  
  2877.  
  2878. /*
  2879.  * Purpose:     Close the ASN stream between a server application process and
  2880.  *              its client.
  2881.  *
  2882.  * Returns:
  2883.  *                -1 if something went wrong
  2884.  *                0, otherwise
  2885.  *
  2886.  *
  2887.  * Description:
  2888.  *              Close the stream by closing the socket and deleting the
  2889.  *              associated data structures.
  2890.  *
  2891.  * Note:
  2892.  *              This routine should only be called by a child application
  2893.  *              process (not by a client).
  2894.  */
  2895.  
  2896. Int2
  2897. NI_CloseASNIO(NI_HandPtr hp)
  2898. {
  2899.     return MsgDestroyHandle(hp);
  2900. } /* NI_CloseANSIO */
  2901.  
  2902.  
  2903.  
  2904. /* MISC FUNCTIONS */
  2905.  
  2906. /* sokselectr and sokselectw are not prototyped in ni_lib.h */
  2907.  
  2908. /*
  2909.  * Purpose:     Wait for a "read" socket to become ready to read, or for
  2910.  *              a timeout to occur.
  2911.  *
  2912.  * Returns:
  2913.  *                -1 if something went wrong
  2914.  *                0, otherwise
  2915.  *
  2916.  *
  2917.  * Description:
  2918.  *              Wait for the indicated "read" socket to be marked as
  2919.  *              "selected" by a socket() call.
  2920.  *
  2921.  * Note:
  2922.  *              This routine is presently unused.
  2923.  *
  2924.  *              The timeout mechanism is not exactly enforced, because
  2925.  *              received signals could result in a longer timeout period.
  2926.  */
  2927.  
  2928. int
  2929. sokselectr(int fd)
  2930. {
  2931.     fd_set      rfds;
  2932.     int         ready;
  2933.     struct timeval      timeout;
  2934.  
  2935.     FD_ZERO(&rfds);
  2936.     FD_SET(fd, &rfds);
  2937.     timeout.tv_sec = NI_SELECT_TIMEOUT;
  2938.     timeout.tv_usec = 0;
  2939.     while ((ready = select(fd+1, &rfds, NULL, NULL, &timeout)) == -1) {
  2940.         switch (SOCK_ERRNO) {
  2941.           case EINTR:
  2942.             continue;
  2943.  
  2944.           default:
  2945.             ni_errno = NIE_MISC;
  2946.             sprintf(ni_errtext, "%s", sys_errlist[SOCK_INDEX_ERRNO]);
  2947.             return -1;
  2948.         }
  2949.     }
  2950.     if (ready == 0) {
  2951.         strcpy(ni_errtext, ni_errlist[ni_errno]);
  2952.         ni_errno = NIE_TIMEOUT;
  2953.         return -1;
  2954.     }
  2955.     if (FD_ISSET(fd, &rfds))
  2956.         return 0;
  2957.     else
  2958.         return -1;
  2959. } /* sokselectr */
  2960.  
  2961.  
  2962.  
  2963. /*
  2964.  * Purpose:     Wait for a "write" socket to become ready to write, or for
  2965.  *              a timeout to occur.
  2966.  *
  2967.  * Returns:
  2968.  *                -1 if something went wrong
  2969.  *                0, otherwise
  2970.  *
  2971.  *
  2972.  * Description:
  2973.  *              Wait for the indicated "write" socket to be marked as
  2974.  *              "selected" by a socket() call.
  2975.  *
  2976.  * Note:
  2977.  *              This routine can be used when waiting for a connect() to go
  2978.  *              through successfully.
  2979.  *
  2980.  *              The timeout mechanism is not exactly enforced, because
  2981.  *              received signals could result in a longer timeout period.
  2982.  */
  2983.  
  2984. int
  2985. sokselectw(int fd, int seconds)
  2986. {
  2987.     fd_set      wfds;
  2988.     int         ready;
  2989.     struct timeval      timeout;
  2990.  
  2991.     FD_ZERO(&wfds);
  2992.     FD_SET(fd, &wfds);
  2993.     timeout.tv_sec = NI_SELECT_TIMEOUT;
  2994.     if (seconds > 0) /* override default */
  2995.         timeout.tv_sec = seconds;
  2996.     timeout.tv_usec = 0;
  2997.     while ((ready = select(fd+1, NULL, &wfds, NULL, &timeout)) == -1) {
  2998.         switch (SOCK_ERRNO) {
  2999.           case EINTR:
  3000.             continue;
  3001.  
  3002.           default:
  3003.             ni_errno = NIE_MISC;
  3004.             sprintf(ni_errtext, "%s", sys_errlist[SOCK_INDEX_ERRNO]);
  3005.             return -1;
  3006.         }
  3007.     }
  3008.     if (ready == 0) {
  3009.         strcpy(ni_errtext, ni_errlist[ni_errno]);
  3010.         ni_errno = NIE_TIMEOUT;
  3011.         return -1;
  3012.     }
  3013.     if (FD_ISSET(fd, &wfds))
  3014.     {
  3015. #ifdef OS_UNIX
  3016.         int err;
  3017.         int optlen;
  3018.  
  3019.         optlen = sizeof(int);
  3020.         if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *) &err, &optlen) >= 0 &&
  3021.             err != 0) /* check for an error */
  3022.             return -1; /* got some error */
  3023. #endif /* OS_UNIX */
  3024.         return 0;
  3025.     }
  3026.     else
  3027.         return -1;
  3028. } /* sokselectw */
  3029.  
  3030.  
  3031.  
  3032. /*
  3033.  * Purpose:     Parse the error number from an ASN error string which was
  3034.  *              formatted at a low level.
  3035.  *
  3036.  * Returns:
  3037.  *                -1, if unable to parse the string
  3038.  *                the parsed error number, otherwise
  3039.  *
  3040.  *
  3041.  * Description:
  3042.  *              Parse the error number from an ASN error string which was
  3043.  *              prepared by the ASN tools, and formatted at a low level.
  3044.  *
  3045.  * Note:
  3046.  *              The parsing mechanism is dependent upon any future format
  3047.  *              changes which may occur in the ASN tools.
  3048.  */
  3049.  
  3050. int
  3051. getAsnError(char *str)
  3052. {
  3053.     int         errnum;
  3054.  
  3055.     if (sscanf(str, "%*s %*s %*s [-%d]", &errnum) < 1)
  3056.         errnum = -1;
  3057.     return errnum;
  3058. } /* getAsnError */
  3059.  
  3060.  
  3061. /*
  3062.  * Purpose:     Set the Connection ID file pointer
  3063.  *
  3064.  * Parameters:
  3065.  *   fp           The new value for the file descriptor
  3066.  *
  3067.  *
  3068.  * Description:
  3069.  *              Set the Connection ID file pointer. This is used to update
  3070.  *              the connection ID each time it is updated, to keep the
  3071.  *              value current.
  3072.  *
  3073.  * Note:
  3074.  *              In reality, this should only be called by the dispatcher.
  3075.  */
  3076.  
  3077. void
  3078. SetConFilePtr (FILE *fp)
  3079. {
  3080.     conid_fp = fp;
  3081. }
  3082.  
  3083.  
  3084. /*
  3085.  * Purpose:     Update the connection ID file
  3086.  *
  3087.  * Parameters:
  3088.  *   conid        The new connection ID value
  3089.  *
  3090.  *
  3091.  * Description:
  3092.  *              Update the connection ID file, and be sure to flush the stream,
  3093.  *              to try to ensure that output really occurs.
  3094.  *
  3095.  * Note:
  3096.  *              Should be called every time the "next" connection ID is
  3097.  *              modified.
  3098.  */
  3099.  
  3100. void
  3101. WriteConFile (Uint4 conid)
  3102. {
  3103.     if (conid_fp != NULL) {
  3104.         (void) fseek(conid_fp, 0L, SEEK_SET);
  3105.         (void) FileWrite((CharPtr) &conid, 1, sizeof(conid), conid_fp);
  3106.         (void) fflush (conid_fp);
  3107.     }
  3108. }
  3109.  
  3110.  
  3111. /*
  3112.  * Purpose:     Close the connection ID file
  3113.  *
  3114.  *
  3115.  * Description:
  3116.  *              Close the connection ID file.
  3117.  *              to try to ensure that output really occurs.
  3118.  *
  3119.  * Note:
  3120.  *              In reality, this should only be called by the dispatcher.
  3121.  */
  3122.  
  3123. void
  3124. CloseConFile (void)
  3125. {
  3126.     if (conid_fp != NULL) {
  3127.         fclose (conid_fp);
  3128.         conid_fp = NULL;
  3129.     }
  3130. }
  3131.  
  3132.  
  3133.  
  3134. /*
  3135.  * Purpose:     Check for expired timers
  3136.  *
  3137.  *
  3138.  * Description:
  3139.  *              For every expired timer, call the specified timer
  3140.  *              callback function, which is in turn responsible for cancelling
  3141.  *              the timer.
  3142.  *
  3143.  * Note:
  3144.  *              Timer checks only take place when this function is called.
  3145.  *              Therefore, it is the responsibility of an application to
  3146.  *              intermitently call this function.  This could be done, e.g.
  3147.  *              using the UNIX alarm clock mechanism, or inside of an event
  3148.  *              loop.
  3149.  *
  3150.  *              The order of operations is significant here, because the
  3151.  *              hook function must cancel the timer.  To perform the linked
  3152.  *              list traversal in a less careful manner could result in
  3153.  *              illegal memory accesses.
  3154.  *
  3155.  *              The timer list in managed in a very unsophisticated manner;
  3156.  *              if lots of timers were anticipated, the list would be
  3157.  *              maintained sorted by time, and all of the timer functions
  3158.  *              would need to maintain and traverse the timer list based
  3159.  *              upon this criterion.
  3160.  *
  3161.  *              A count is used as a failsafe mechanism against infinite loops.
  3162.  */
  3163.  
  3164. #define NI_MAX_TIMERS 1000
  3165.  
  3166. void
  3167. NI_ProcessTimers(void)
  3168. {
  3169.     NodePtr t;
  3170.     NodePtr tnew;
  3171.     NI_TimerPtr timer;
  3172.     time_t curtime;
  3173.     int count = NI_MAX_TIMERS;
  3174.  
  3175.     if ((t = timerHead) == NULL)
  3176.     {
  3177.         return;
  3178.     }
  3179.  
  3180.     curtime = GetSecs();
  3181.  
  3182.     do {
  3183.         timer = (NI_TimerPtr) t->elem;
  3184.         tnew = ListGetNext(t);
  3185.         if (timer != NULL && timer->timeout != NULLB &&
  3186.             timer->timeout <= curtime)
  3187.         { /* fire timer */
  3188.             /* mark the timer so it won't fire again */
  3189.             timer->timeout = NULLB;
  3190.             if (timer->hook != NULL)
  3191.             {
  3192.                 timer->hook(timer->hookParam);
  3193.             }
  3194.         }
  3195.         if (t == tnew)
  3196.         { /* data structure error, time to bail out */
  3197.             break;
  3198.         }
  3199.         t = tnew;
  3200.     } while (t != timerHead && t != NULL && --count > 0);
  3201. }
  3202.  
  3203.  
  3204. /*
  3205.  * Purpose:     Return the time when the next timeout will occur
  3206.  *
  3207.  * Returns:     The time, in seconds, when the next scheduled timeout will
  3208.  *              occur, or NULLB, if there are no timers set.
  3209.  *
  3210.  * Description:
  3211.  *              Return the time when the next timer timeout will occur.
  3212.  *              This information is typically used with the select()
  3213.  *              system call, to ensure that a timeout parameter is passed
  3214.  *              to select() which is sufficiently short to ensure that
  3215.  *              the application will call NI_ProcessTimers() at an
  3216.  *              appropriate time.
  3217.  *
  3218.  * Note:
  3219.  *              The timer list in managed in a very unsophisticated manner;
  3220.  *              if lots of timers were anticipated, the list would be
  3221.  *              maintained sorted by time, and all of the timer functions
  3222.  *              would need to maintain and traverse the timer list based
  3223.  *              upon this criterion.
  3224.  */
  3225.  
  3226. time_t
  3227. NI_GetNextWakeup(void)
  3228. {
  3229.     time_t next_wakeup = NULLB;
  3230.     NodePtr t;
  3231.     NI_TimerPtr timer;
  3232.  
  3233.     NI_ProcessTimers();
  3234.  
  3235.     if ((t = timerHead) == NULL)
  3236.     {
  3237.         return NULLB;
  3238.     }
  3239.  
  3240.     do {
  3241.         t = ListGetNext(t);
  3242.         timer = (NI_TimerPtr) t->elem;
  3243.         if (next_wakeup == NULLB || (timer->timeout != NULLB &&
  3244.             timer->timeout < next_wakeup))
  3245.         {
  3246.             next_wakeup = timer->timeout;
  3247.         }
  3248.     } while (t != timerHead && t != NULL);
  3249.  
  3250.     return next_wakeup;
  3251. }
  3252.  
  3253.  
  3254. /*
  3255.  * Purpose:     Set a timer
  3256.  *
  3257.  * Parameters:
  3258.  *   timeout      The time in seconds when the timer should expire
  3259.  *   hook         Callback to be called when (if) the timer expires
  3260.  *   hookParam    Parameter to be passed to caller's hook when the timer expires
  3261.  *
  3262.  *
  3263.  * Returns:     The "timer ID", really a pointer to the timer data structure
  3264.  *
  3265.  *
  3266.  * Description:
  3267.  *              Sets a timer with the appropriate parameters.
  3268.  *
  3269.  * Note:
  3270.  *              The timer list in managed in a very unsophisticated manner;
  3271.  *              if lots of timers were anticipated, the list would be
  3272.  *              maintained sorted by time, and all of the timer functions
  3273.  *              would need to maintain and traverse the timer list based
  3274.  *              upon this criterion.
  3275.  *
  3276.  *              It is the responsibility of the application (usually the
  3277.  *              hook function) to cancel the timer.
  3278.  */
  3279.  
  3280. NodePtr
  3281. NI_SetTimer(time_t timeout, NI_TimeoutHook hook, Pointer hookParam)
  3282. {
  3283.     NodePtr t;
  3284.     NI_TimerPtr timer;
  3285.  
  3286.     timer = (NI_TimerPtr) MemNew(sizeof(NI_Timer));
  3287.     timer->timeout = timeout;
  3288.     timer->hook = hook;
  3289.     timer->hookParam = hookParam;
  3290.     t = ListInsert(timer, timerHead);
  3291.     timerHead = t;
  3292.  
  3293.     return t;
  3294. }
  3295.  
  3296.  
  3297. /*
  3298.  * Purpose:     Cancel a timer
  3299.  *
  3300.  * Parameters:
  3301.  *   timerID      The ID of the timer
  3302.  *
  3303.  *
  3304.  * Description:
  3305.  *              Cancel the specified timer by deleting the entry and its
  3306.  *              associated data structure.
  3307.  *
  3308.  * Note:
  3309.  *              The timer list in managed in a very unsophisticated manner;
  3310.  *              if lots of timers were anticipated, the list would be
  3311.  *              maintained sorted by time, and all of the timer functions
  3312.  *              would need to maintain and traverse the timer list based
  3313.  *              upon this criterion.
  3314.  */
  3315.  
  3316. void
  3317. NI_CancelTimer(NodePtr timerId)
  3318. {
  3319.     if (timerId != NULL)
  3320.     {
  3321.         MemFree (timerId->elem);
  3322.         timerHead = ListDelete(timerId);
  3323.     }
  3324.  
  3325.     NI_ProcessTimers();
  3326. }
  3327.  
  3328.  
  3329. /*
  3330.  * Purpose:     Set an activity hook, to inform the application of key events
  3331.  *
  3332.  * Parameters:
  3333.  *   hook         The hook (callback function)
  3334.  *
  3335.  *
  3336.  * Description:
  3337.  *              Setup a hook function which will subsequently be used to
  3338.  *              inform the application of various events; these currently
  3339.  *              include:
  3340.  *                * Connection to dispatcher
  3341.  *                * Disconnection from dispatcher
  3342.  *                * Service connection
  3343.  *                * Service disconnection
  3344.  *                * Bytes written
  3345.  *                * Bytes read
  3346.  *
  3347.  * Note:
  3348.  *              This hook is global for the running application.
  3349.  */
  3350.  
  3351. void
  3352. NI_SetActivityHook (NI_NetServHook hook)
  3353. {
  3354.     activityHook = hook;
  3355. }
  3356.  
  3357.  
  3358. /*
  3359.  * Purpose:     Return the current activity hook
  3360.  *
  3361.  *
  3362.  * Description:
  3363.  *              Return the current activity hook.  This is only intended
  3364.  *              to be used internally by the Network Services library.
  3365.  *              This function is used to avoid making activityHook into a
  3366.  *              global variable.
  3367.  */
  3368.  
  3369. NI_NetServHook
  3370. NI_ActivityHook (void)
  3371. {
  3372.     return activityHook;
  3373. }
  3374.  
  3375.  
  3376. /*
  3377.  * Purpose:     Initialize socket management
  3378.  *
  3379.  * Description:
  3380.  *              If not already initialized, initialize the socket management
  3381.  *              data structures
  3382.  */
  3383.  
  3384. static void
  3385. InitLogSocket()
  3386. {
  3387.     static Boolean inited = FALSE;
  3388.  
  3389.     if (! inited)
  3390.     {
  3391.         FD_ZERO(&openfds);
  3392.         inited = TRUE;
  3393.     }
  3394. }
  3395.  
  3396.  
  3397. /*
  3398.  * Purpose:     Count the number of open sockets
  3399.  */
  3400.  
  3401. Int2
  3402. NI_SocketsOpen(void)
  3403. {
  3404.     int sok;
  3405.     int count = 0;
  3406.  
  3407.     InitLogSocket();
  3408.     for (sok = 0; sok < FD_SETSIZE; sok++)
  3409.     {
  3410.         if (FD_ISSET(sok, &openfds))
  3411.             count++;
  3412.     }
  3413.     return count;
  3414. }
  3415.  
  3416. /*
  3417.  * Purpose:     Log each socket transaction
  3418.  */
  3419.  
  3420. void
  3421. NI_LogSocket(int sok, Boolean opening, CharPtr filename, int lineno)
  3422. {
  3423.     int localsok;
  3424.  
  3425.     InitLogSocket();
  3426.  
  3427.     if (sok == INVALID_SOCKET || sok < 0 || sok >= FD_SETSIZE)
  3428.     {
  3429. #ifndef NETP_INET_WSOCK
  3430.         /* FD_SETSIZE doesn't accurately describe the socket range for
  3431.            WinSock applications, so don't generate misleading error msgs */
  3432.         ErrPostEx(SEV_WARNING,0,0, "Bad %s operation on socket %d at %s:%d",
  3433.                   opening ? "opening" : "closing", sok, filename, lineno);
  3434. #endif /* NETP_INET_WSOCK */
  3435.         return;
  3436.     }
  3437.  
  3438.     if (opening)
  3439.     {
  3440.         TRACE("Just opened socket %d at %s:%d\n", sok, filename, lineno);
  3441.         if (FD_ISSET(sok, &openfds))
  3442.         {
  3443.             ErrPostEx(SEV_ERROR,0,0, "Duplicate open of socket %d at %s:%d",
  3444.                       sok, filename, lineno);
  3445.         } else {
  3446.             FD_SET(sok, &openfds);
  3447.         }
  3448.     } else {
  3449.         TRACE("Trying to close socket %d at %s:%d\n", sok, filename, lineno);
  3450.         if (FD_ISSET(sok, &openfds))
  3451.         {
  3452.             FD_CLR(sok, &openfds);
  3453.         } else {
  3454.             ErrPostEx(SEV_ERROR,0,0, "Duplicate close of socket %d at %s:%d",
  3455.                       sok, filename, lineno);
  3456.         }
  3457.     }
  3458.  
  3459. #ifdef DEBUG
  3460.     for (localsok = 0; localsok < FD_SETSIZE; localsok++)
  3461.     {
  3462.         if (FD_ISSET(localsok, &openfds))
  3463.         {
  3464.             TRACE("Socket %d is currently open\n", localsok);
  3465.         }
  3466.     }
  3467. #endif /* DEBUG */
  3468. }
  3469.  
  3470.